feat(permissions): add `--yolo` flag for auto-accepting all permissions

hems , Crush , and Andrey Nering created

- Add --yolo/-y flag to enable dangerous mode that automatically accepts
  all permission requests
- Pass yolo flag from command line through config to permission service

This feature allows users to bypass all permission prompts when they
trust the operations being performed, useful for automation or when
running in controlled environments.

💘 Generated with Crush
Co-Authored-By: Crush <noreply@crush.charm.land>
Co-authored-by: Andrey Nering <andreynering@users.noreply.github.com>

Change summary

cmd/root.go                       |  6 ++++++
internal/app/app.go               |  3 ++-
internal/config/config.go         | 14 +++++++-------
internal/permission/permission.go |  8 +++++++-
4 files changed, 22 insertions(+), 9 deletions(-)

Detailed changes

cmd/root.go 🔗

@@ -45,6 +45,9 @@ to assist developers in writing, debugging, and understanding code directly from
 
   # Run a single non-interactive prompt with JSON output format
   crush -p "Explain the use of context in Go" -f json
+
+  # Run in dangerous mode (auto-accept all permissions)
+  crush -y
   `,
 	RunE: func(cmd *cobra.Command, args []string) error {
 		// Load the config
@@ -52,6 +55,7 @@ to assist developers in writing, debugging, and understanding code directly from
 		cwd, _ := cmd.Flags().GetString("cwd")
 		prompt, _ := cmd.Flags().GetString("prompt")
 		quiet, _ := cmd.Flags().GetBool("quiet")
+		yolo, _ := cmd.Flags().GetBool("yolo")
 
 		if cwd != "" {
 			err := os.Chdir(cwd)
@@ -71,6 +75,7 @@ to assist developers in writing, debugging, and understanding code directly from
 		if err != nil {
 			return err
 		}
+		cfg.Options.SkipPermissionsRequests = yolo
 
 		ctx := cmd.Context()
 
@@ -152,6 +157,7 @@ func init() {
 	rootCmd.Flags().BoolP("help", "h", false, "Help")
 	rootCmd.Flags().BoolP("debug", "d", false, "Debug")
 	rootCmd.Flags().StringP("prompt", "p", "", "Prompt to run in non-interactive mode")
+	rootCmd.Flags().BoolP("yolo", "y", false, "Automatically accept all permissions (dangerous mode)")
 
 	// Add quiet flag to hide spinner in non-interactive mode
 	rootCmd.Flags().BoolP("quiet", "q", false, "Hide spinner in non-interactive mode")

internal/app/app.go 🔗

@@ -58,12 +58,13 @@ func New(ctx context.Context, conn *sql.DB, cfg *config.Config) (*App, error) {
 	sessions := session.NewService(q)
 	messages := message.NewService(q)
 	files := history.NewService(q, conn)
+	skipPermissionsRequests := cfg.Options != nil && cfg.Options.SkipPermissionsRequests
 
 	app := &App{
 		Sessions:    sessions,
 		Messages:    messages,
 		History:     files,
-		Permissions: permission.NewPermissionService(cfg.WorkingDir()),
+		Permissions: permission.NewPermissionService(cfg.WorkingDir(), skipPermissionsRequests),
 		LSPClients:  make(map[string]*lsp.Client),
 
 		globalCtx: ctx,

internal/config/config.go 🔗

@@ -114,13 +114,13 @@ type TUIOptions struct {
 }
 
 type Options struct {
-	ContextPaths         []string    `json:"context_paths,omitempty"`
-	TUI                  *TUIOptions `json:"tui,omitempty"`
-	Debug                bool        `json:"debug,omitempty"`
-	DebugLSP             bool        `json:"debug_lsp,omitempty"`
-	DisableAutoSummarize bool        `json:"disable_auto_summarize,omitempty"`
-	// Relative to the cwd
-	DataDirectory string `json:"data_directory,omitempty"`
+	ContextPaths            []string    `json:"context_paths,omitempty"`
+	TUI                     *TUIOptions `json:"tui,omitempty"`
+	Debug                   bool        `json:"debug,omitempty"`
+	DebugLSP                bool        `json:"debug_lsp,omitempty"`
+	DisableAutoSummarize    bool        `json:"disable_auto_summarize,omitempty"`
+	DataDirectory           string      `json:"data_directory,omitempty"` // Relative to the cwd
+	SkipPermissionsRequests bool        `json:"-"`                        // Automatically accept all permissions (YOLO mode)
 }
 
 type MCPs map[string]MCPConfig

internal/permission/permission.go 🔗

@@ -49,6 +49,7 @@ type permissionService struct {
 	pendingRequests       sync.Map
 	autoApproveSessions   []string
 	autoApproveSessionsMu sync.RWMutex
+	skip                  bool
 }
 
 func (s *permissionService) GrantPersistent(permission PermissionRequest) {
@@ -77,6 +78,10 @@ func (s *permissionService) Deny(permission PermissionRequest) {
 }
 
 func (s *permissionService) Request(opts CreatePermissionRequest) bool {
+	if s.skip {
+		return true
+	}
+
 	s.autoApproveSessionsMu.RLock()
 	autoApprove := slices.Contains(s.autoApproveSessions, opts.SessionID)
 	s.autoApproveSessionsMu.RUnlock()
@@ -125,10 +130,11 @@ func (s *permissionService) AutoApproveSession(sessionID string) {
 	s.autoApproveSessionsMu.Unlock()
 }
 
-func NewPermissionService(workingDir string) Service {
+func NewPermissionService(workingDir string, skip bool) Service {
 	return &permissionService{
 		Broker:             pubsub.NewBroker[PermissionRequest](),
 		workingDir:         workingDir,
 		sessionPermissions: make([]PermissionRequest, 0),
+		skip:               skip,
 	}
 }