Doramagic Project Pack ยท Human Manual

crush

| Component | Purpose | Location | |-----------|---------|----------| | App | Main application state and coordination | internal/app/app.go | | Backend | Workspace and session management |...

Getting Started with Crush

Related topics: Configuration Guide, LLM Provider Setup

Section Related Pages

Continue reading this section for the full explanation and source context.

Section macOS

Continue reading this section for the full explanation and source context.

Section Linux

Continue reading this section for the full explanation and source context.

Section Windows

Continue reading this section for the full explanation and source context.

Related topics: Configuration Guide, LLM Provider Setup

Getting Started with Crush

Crush is your AI-powered coding assistant that runs directly in your terminal. It connects to your tools, code, and workflows, integrating seamlessly with your preferred LLM provider to assist with development tasks ranging from code editing to complex multi-step operations.

What is Crush?

Crush is a terminal-based AI coding agent built by Charm that provides:

  • Multi-Model Support: Connect to a wide range of LLMs including Claude, DeepSeek, OpenAI, Gemini, and custom OpenAI- or Anthropic-compatible APIs
  • Integrated Tool Access: Execute bash commands, edit files, search codebases, and manage git operations
  • LSP Integration: Leverage Language Server Protocols for enhanced context awareness
  • MCP Support: Connect to Model Context Protocol servers for extended capabilities
  • Agent Skills: Extend functionality through reusable skill packages following the Agent Skills open standard

The architecture follows a client-server model where the server manages workspaces, sessions, and state while clients can connect remotely. Source: internal/app/app.go:19-50

Installation

Crush supports multiple platforms and installation methods.

macOS

# Homebrew
brew install charmbracelet/tap/crush

# MacPorts
sudo port install crush

Linux

# Binary installer
curl -fsSL https://charm.sh/crush/install.sh | sh

# via nix
nix run github:charmbracelet/crush

# FreeBSD
pkg install crush

Windows

# Winget
winget install charmbracelet.crush

# Scoop
scoop bucket add charm https://github.com/charmbracelet/scoop-bucket.git
scoop install crush

Nix/NixOS

Crush is available via the official NUR in nur.repos.charmbracelet.crush. NixOS and Home Manager modules are also available:

{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
    nur.url = "github:nix-community/NUR";
  };
}

Source: README.md

First Launch

Upon first launch, Crush creates its configuration and data directories:

PlatformConfig LocationData Location
Unix$HOME/.config/crush/crush.json$HOME/.local/share/crush/crush.json
Windows%LOCALAPPDATA%\crush\crush.json%LOCALAPPDATA%\crush\crush.json

You can override these locations using environment variables:

  • CRUSH_GLOBAL_CONFIG - Override config directory
  • CRUSH_GLOBAL_DATA - Override data directory

Source: README.md

Configuration

Crush runs great with no configuration. When needed, configuration is stored as JSON in your project or home directory with the following priority:

  1. .crush.json (project-local)
  2. crush.json (project root)
  3. $HOME/.config/crush/crush.json (global)

Basic Configuration Structure

{
  "$schema": "https://charm.land/crush.json",
  "model": {
    "large": "claude-sonnet-4-20250514",
    "small": "claude-haiku-4-20250514"
  },
  "providers": {
    "deepseek": {
      "type": "openai-compat",
      "base_url": "https://api.deepseek.com/v1",
      "api_key": "$DEEPSEEK_API_KEY"
    }
  }
}

Configuration Scopes

The backend supports configuration at different scopes. Use the SetConfigField method to modify settings:

func (b *Backend) SetConfigField(workspaceID string, scope config.Scope, key string, value any) error

Configuration changes are published to all subscribers via the event system, ensuring remote clients refresh their cached config snapshot. Source: internal/backend/config.go:25-40

Language Server Protocol (LSP) Integration

Crush can use LSPs to provide additional context for informed decision-making. LSPs are configured in your crush.json:

{
  "$schema": "https://charm.land/crush.json",
  "lsp": {
    "go": {
      "command": "gopls",
      "env": {
        "GOTOOLCHAIN": "go1.24.5"
      }
    },
    "typescript": {
      "command": "typescript-language-server",
      "args": ["--stdio"]
    },
    "nix": {
      "command": "nil"
    }
  }
}

LSP Event System

The LSP manager tracks server states and emits events:

Event TypeDescription
state_changedLSP server state changed (connecting, connected, disconnected)
diagnostics_changedDiagnostics for a language were updated

Source: internal/app/lsp_events.go:10-45

Accessing LSP Data

// Get all LSP states
states := app.GetLSPStates()

// Get diagnostics for a specific LSP
diagnostics, err := backend.GetLSPDiagnostics(workspaceID, lspName)

Model Context Protocol (MCP) Servers

Crush supports MCP servers through three transport types:

TransportUse Case
stdioCommand-line MCP servers
httpHTTP endpoints
sseServer-Sent Events endpoints

Configuration Example

{
  "mcp": {
    "servers": {
      "my-server": {
        "transport": "stdio",
        "command": "npx",
        "args": ["-y", "@some/mcp-server"]
      }
    }
  }
}

Shell-style value expansion works in command, args, env, headers, and url:

  • $VAR - Environment variable
  • ${VAR:-default} - With fallback
  • $(cat /path/to/secret) - Command substitution

Source: README.md

Agent Skills

Crush supports the Agent Skills open standard for extending capabilities. Skills are folders containing a SKILL.md file.

Skill Search Paths

Global skill paths (checked in order):

PriorityUnix PathWindows Path
1$CRUSH_SKILLS_DIR$CRUSH_SKILLS_DIR
2$XDG_CONFIG_HOME/agents/skills%LOCALAPPDATA%\agents\skills\
3$XDG_CONFIG_HOME/crush/skills%LOCALAPPDATA%\crush\skills\
4~/.agents/skills/%USERPROFILE%\AppData\Local\agents\skills\
5~/.claude/skills/%USERPROFILE%\AppData\Local\crush\skills\

Project-local paths (relative to project root):

  • .agents/skills
  • .crush/skills
  • .claude/skills
  • .cursor/skills

Adding Custom Skill Paths

{
  "$schema": "https://charm.land/crush.json",
  "options": {
    "skills_paths": [
      "~/.config/crush/skills",
      "./project-skills"
    ]
  }
}

Source: README.md and internal/commands/commands.go:25-50

Custom Providers

Crush supports custom provider configurations for both OpenAI-compatible and Anthropic-compatible APIs.

OpenAI-Compatible APIs

Use openai-compat type for non-OpenAI providers with OpenAI-compatible APIs:

{
  "$schema": "https://charm.land/crush.json",
  "providers": {
    "deepseek": {
      "type": "openai-compat",
      "base_url": "https://api.deepseek.com/v1",
      "api_key": "$DEEPSEEK_API_KEY"
    },
    "openrouter": {
      "type": "openai-compat",
      "base_url": "https://openrouter.ai/api/v1",
      "api_key": "$OPENROUTER_API_KEY"
    }
  }
}

Provider Type Selection

[!NOTE]
Choose the correct provider type:
- openai - For proxying/routing through OpenAI
- openai-compat - For non-OpenAI providers with OpenAI-compatible APIs

Model Specification

Models can be specified in multiple formats:

  • "claude-sonnet-4-20250514" - Direct model ID
  • "deepseek-chat" - Provider/model format
  • "synthetic/moonshot/kimi-k2" - Nested provider path

Source: internal/app/provider.go:10-40

Server-Client Architecture

Crush uses an experimental client-server architecture that can be enabled with:

CRUSH_CLIENT_SERVER=1 crush

Connection Methods

ProtocolUnixWindows
Unix Socketunix:///path/to/crush.sock-
Named Pipe-npipe:////./pipe/crush
TCPtcp://host:porttcp://host:port

The default host creates a user-specific socket:

func DefaultHost() string {
    sock := "crush.sock"
    usr, _ := user.Current()
    if usr.Uid != "" {
        sock = fmt.Sprintf("crush-%s.sock", usr.Uid)
    }
    if runtime.GOOS == "windows" {
        return fmt.Sprintf("npipe:////./pipe/%s", sock)
    }
    return fmt.Sprintf("unix:///%s/%s", os.UserHomeDir(), sock)
}

Source: internal/server/server.go:35-55

Remote Client Usage

// Connect to default server
client, err := client.DefaultClient("/path/to/workspace")

// List workspaces
workspaces, err := client.ListWorkspaces(ctx)

// Subscribe to events
events, err := backend.SubscribeEvents(ctx, workspaceID)

Common Workflows

Workflow: Setting Up DeepSeek

# 1. Install Crush
curl -fsSL https://charm.sh/crush/install.sh | sh

# 2. Set API key
export DEEPSEEK_API_KEY="your-api-key"

# 3. Create config
cat > ~/.config/crush/crush.json << 'EOF'
{
  "$schema": "https://charm.land/crush.json",
  "model": {
    "large": "deepseek-chat",
    "small": "deepseek-coder"
  },
  "providers": {
    "deepseek": {
      "type": "openai-compat",
      "base_url": "https://api.deepseek.com/v1",
      "api_key": "$DEEPSEEK_API_KEY"
    }
  }
}
EOF

# 4. Launch
crush

Workflow: Adding LSP for Go Development

{
  "lsp": {
    "go": {
      "command": "gopls",
      "env": {
        "GOTOOLCHAIN": "auto"
      }
    }
  }
}

Workflow: Using MCP Servers

{
  "mcp": {
    "servers": {
      "filesystem": {
        "transport": "stdio",
        "command": "npx",
        "args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/dir"]
      }
    }
  }
}

Architecture Overview

graph TD
    A[User Terminal] --> B[Crush Client]
    B --> C[Crush Server]
    C --> D[Workspace Manager]
    D --> E[App Instance]
    E --> F[Agent Coordinator]
    F --> G[Tool Executor]
    G --> H[LSP Manager]
    G --> I[MCP Client]
    G --> J[Skills Manager]
    C --> K[HTTP/SSE]
    B --> K

Core Components

ComponentPurposeLocation
AppMain application state and coordinationinternal/app/app.go
BackendWorkspace and session managementinternal/backend/
ServerHTTP/Unix socket serverinternal/server/
ClientRemote client connectioninternal/client/
LSP ManagerLanguage server protocol integrationinternal/lsp/
Skills ManagerAgent skills discovery and loadinginternal/skills/

Source: internal/app/app.go:19-50

Troubleshooting

Database Corruption

If you experience database corruption errors, check:

  1. Filesystem integrity with fsck
  2. Disk health with SMART tools
  3. Ensure you're not running multiple instances simultaneously that share the same data directory

LSP Configuration Not Working

For global LSP config issues:

  1. Verify the config path: ~/.config/crush/crush.json
  2. Check the LSP command is in your PATH
  3. Test the LSP directly: gopls version (for Go)

OpenRouter/API Authentication Errors

# Verify your API key is set
echo $OPENROUTER_API_KEY

# Test the key directly
curl -H "Authorization: Bearer $OPENROUTER_API_KEY" \
  https://openrouter.ai/api/v1/models

Bash Tool Hanging

Recent versions fixed issues with interactive commands like git rebase -i. If you experience hangs:

  1. Update to the latest version
  2. Avoid running truly interactive commands through the bash tool
  3. Use dedicated tools (git tool) for git operations

Source: Community Issues

Next Steps

  • Configure your preferred model in crush.json
  • Add LSP support for your primary language
  • Explore MCP servers for extended capabilities
  • Create custom skills by adding SKILL.md files to your skills directories
  • Enable client-server mode with CRUSH_CLIENT_SERVER=1 for remote access

For more advanced configuration, ask Crush to configure itself using the built-in crush-config skill.

Source: https://github.com/charmbracelet/crush / Human Manual

Project Structure

Related topics: Getting Started with Crush

Section Related Pages

Continue reading this section for the full explanation and source context.

Section Core Application

Continue reading this section for the full explanation and source context.

Section Server Layer

Continue reading this section for the full explanation and source context.

Section Client Layer

Continue reading this section for the full explanation and source context.

Related topics: Getting Started with Crush

Project Structure

Overview

Crush is a terminal-based AI coding assistant built by Charm that integrates tools, code, and workflows with LLM models. The project follows a client-server architecture where the core logic runs as a backend server, with clients (including CLI and potentially IDE integrations) communicating via HTTP APIs.

The architecture enables:

  • Multi-workspace support - Multiple project directories can be managed simultaneously
  • Session persistence - Conversations and context are stored in a database
  • Extensible tooling - LSPs (Language Server Protocols) and MCPs (Model Context Protocols) enhance agent capabilities
  • Multi-provider model support - Various LLM providers can be configured and switched at runtime

High-Level Architecture

graph TD
    subgraph "Client Layer"
        CLI[CLI / TUI Client]
        IDE[IDE Integration]
    end
    
    subgraph "Server Layer"
        Server[HTTP Server]
        Controller[API Controller]
    end
    
    subgraph "Backend Core"
        Backend[Backend Manager]
        Workspace[Workspace Manager]
        Session[Session Manager]
        Permission[Permission System]
    end
    
    subgraph "Data Layer"
        DB[(Database)]
        Config[Config Store]
    end
    
    subgraph "External Integrations"
        LSP[LSP Servers]
        MCP[MCP Servers]
        LLM[LLM Providers]
    end
    
    CLI --> Server
    IDE --> Server
    Server --> Controller
    Controller --> Backend
    Backend --> Workspace
    Backend --> Session
    Backend --> Permission
    Backend --> DB
    Backend --> Config
    Backend --> LSP
    Backend --> MCP
    Backend --> LLM

Directory Structure

Core Application

PathPurpose
main.goApplication entry point
internal/app/Application lifecycle and shutdown management
internal/backend/Core backend logic and workspace management
internal/db/Database operations and persistence
internal/config/Configuration management and stores

Server Layer

PathPurpose
internal/server/HTTP server implementation
internal/server/server.goServer initialization and routing
internal/server/config.goConfiguration API handlers
internal/server/events.goEvent conversion and SSE (Server-Sent Events)
internal/server/proto.goProtocol definitions and API contracts

Client Layer

PathPurpose
internal/client/Client library for server communication
internal/client/config.goClient-side configuration operations

Server Architecture

The server layer implements a RESTful API with support for Server-Sent Events (SSE) for real-time updates.

Route Registration

Routes are registered in internal/server/server.go using Go's http.ServeMux:

mux.HandleFunc("GET /v1/health", c.handleGetHealth)
mux.HandleFunc("GET /v1/version", c.handleGetVersion)
mux.HandleFunc("GET /v1/config", c.handleGetConfig)
mux.HandleFunc("POST /v1/control", c.handlePostControl)
mux.HandleFunc("GET /v1/workspaces", c.handleGetWorkspaces)

Source: internal/server/server.go:1-50

API Endpoints

The server exposes the following endpoint categories:

CategoryEndpointsDescription
SystemGET /v1/health, GET /v1/version, POST /v1/controlServer health, version, and control commands
ConfigurationGET /v1/config, workspace config endpointsGlobal and workspace-specific configuration
WorkspacesGET /v1/workspaces, workspace CRUDWorkspace management
ProjectProject initialization, promptsProject setup and context

Event System

Events flow from the backend to clients via SSE envelopes:

type envelope struct {
    Type    string      `json:"type"`
    Payload interface{} `json:"payload"`
}

Source: internal/server/events.go:1-30

Supported event types include:

  • LSP Events - Language server diagnostics and state changes
  • MCP Events - Model Context Protocol server status
  • Permission Events - Tool execution authorization requests and notifications
  • Skills Events - Agent skill discovery and loading states

Backend Architecture

The backend (internal/backend/) is the core of Crush's functionality.

Workspace Management

Workspaces are identified by unique UUIDs and resolved paths:

type Workspace struct {
    ID           string
    Path         string
    resolvedPath string
    clients      map[string]*clientState
    shutdownFn   func()
}

Source: internal/backend/backend_test.go:1-20

Key workspace operations:

  • Path Resolution - Handles absolute paths and symlinks
  • Client Tracking - Manages multiple client connections per workspace
  • Graceful Shutdown - Coordinates cleanup when workspaces are removed

Client State Management

Each connected client has an associated state:

type clientState struct {
    ID          string
    WorkspaceID string
    LastSeen    time.Time
    // ...
}

Backend Lifecycle

Backend shutdown follows a graceful pattern:

s.backend.Shutdown()

The server shutdown callback triggers a coordinated shutdown sequence:

s.backend = backend.New(context.Background(), cfg, func() {
    go func() {
        slog.Info("Shutting down server...")
        if err := s.Shutdown(context.Background()); err != nil {
            slog.Error("Failed to shutdown server", "error", err)
        }
    }()
})

Source: internal/server/server.go:1-50

Application Layer

The application layer (internal/app/) manages the main application lifecycle.

Shutdown Management

The app maintains a list of cleanup functions:

func (app *App) ShutdownForTest() {
    for _, cleanup := range app.cleanupFuncs {
        if cleanup != nil {
            _ = cleanup(context.Background())
        }
    }
    app.cleanupFuncs = nil
}

Source: internal/app/testing.go:1-15

This pattern drives a full production shutdown path including:

  • Database release
  • LSP teardown
  • MCP server shutdown

Configuration System

Configuration Precedence

Crush supports layered configuration with the following priority (highest to lowest):

  1. .crush.json - Project-local configuration
  2. crush.json - Repository-local configuration
  3. $HOME/.config/crush/crush.json - Global user configuration

Configuration Locations

TypeUnix PathWindows Path
Config$HOME/.config/crush/crush.json%LOCALAPPDATA%\crush\crush.json
Data$HOME/.local/share/crush/crush.json%LOCALAPPDATA%\crush\crush.json

Environment Variable Overrides

VariablePurpose
CRUSH_GLOBAL_CONFIGOverride config file location
CRUSH_GLOBAL_DATAOverride data directory location
CRUSH_CLIENT_SERVEREnable experimental client-server mode

Configuration Schema

{
  "$schema": "https://charm.land/crush.json",
  "lsp": { },
  "mcp": { },
  "providers": { },
  "permissions": { },
  "options": { }
}

Client-Server Communication

Client API Operations

The client library (internal/client/config.go) provides methods for:

OperationMethodDescription
Set Preferred ModelSetPreferredModel()Update workspace's default model
Set Compact ModeSetCompactMode()Toggle compact mode for responses
Set Provider API KeySetProviderAPIKey()Configure provider credentials
Import CopilotImportCopilot()OAuth token import for GitHub Copilot
Refresh OAuth TokenRefreshOAuthToken()Token refresh for OAuth providers
MCP ResourcesGetMCPResource()Read MCP server resources
MCP PromptsGetMCPPrompt()Retrieve prompts from MCP servers

API Key Handling

API credentials are transmitted with explicit type information:

switch v := apiKey.(type) {
case string:
    kind = proto.APIKeyKindString
    raw, _ = json.Marshal(v)
case []byte:
    kind = proto.APIKeyKindBytes
    raw = v
}

Source: internal/client/config.go:1-50

This ensures type preservation across the JSON wire format.

Workspace Initialization

Workspaces can be marked as initialized via the API:

// @Summary Mark project as initialized
// @Router /workspaces/{id}/project/init [post]
func (c *controllerV1) handlePostWorkspaceProjectInit(w http.ResponseWriter, r *http.Request)

The backend stores initialization state and can provide initialization prompts:

// @Router /workspaces/{id}/project/init-prompt [get]
func (c *controllerV1) handleGetWorkspaceProjectInitPrompt(w http.ResponseWriter, r *http.Request)

Source: internal/server/config.go:1-50

Event Type Conversion

The events system converts internal event types to wire format for SSE delivery:

func messageToProto(m message.Message) proto.Message {
    msg := proto.Message{
        Role:    proto.MessageRole(m.Role),
        Parts:   make([]any, 0, len(m.Parts)),
    }
    for _, part := range m.Parts {
        switch v := part.(type) {
        case message.TextContent:
            msg.Parts = append(msg.Parts, proto.TextContent{Text: v.Text})
        case message.ToolCall:
            msg.Parts = append(msg.Parts, proto.ToolCall{...})
        }
    }
    return msg
}

Source: internal/server/events.go:1-100

Testing Infrastructure

Backend Test Helpers

The backend provides test utilities for creating isolated workspaces:

func addTestWorkspace(t *testing.T, b *Backend, key string) (*Workspace, *atomic.Int32) {
    var shutdowns atomic.Int32
    ws := &Workspace{
        ID:           uuid.New().String(),
        Path:         key,
        resolvedPath: key,
        clients:      make(map[string]*clientState),
        shutdownFn:   func() { shutdowns.Add(1) },
    }
    b.mu.Lock()
    b.workspaces.Set(ws.ID, ws)
    b.pathIndex[key] = ws.ID
    b.mu.Unlock()
    return ws, &shutdowns
}

Source: internal/backend/backend_test.go:1-30

Event Round-Trip Testing

Events are validated for JSON marshaling round-trips:

func TestSkillsEventToProto_RoundTrip(t *testing.T) {
    env := wrapEvent(src)
    // Validate serialization preserves state values
}

Source: internal/server/events_test.go:1-50

External Integrations

Language Server Protocol (LSP)

Configured LSPs provide context for code understanding:

{
  "lsp": {
    "go": {
      "command": "gopls",
      "env": { "GOTOOLCHAIN": "go1.24.5" }
    }
  }
}

Model Context Protocol (MCP)

MCP servers extend agent capabilities through stdio, HTTP, or SSE transports:

{
  "mcp": {
    "servers": [
      {
        "name": "filesystem",
        "transport": "stdio",
        "command": "npx",
        "args": ["-y", "@modelcontextprotocol/server-filesystem", "."]
      }
    ]
  }
}

Known Issues and Community Feedback

The following community-reported issues relate to the project structure:

IssueDescription
#2935Feature request for multiple permission profiles (Yolo/Safe/Custom) switchable at runtime
#928Requests to remove bash command security limits for unrestricted terminal access
#2898Global LSP config not working - relates to configuration precedence
#2903Database corruption issues in certain environments
#2928OpenRouter model authentication failures

Summary

Crush's architecture separates concerns into:

  1. Client Layer - CLI/TUI interfaces that communicate with the server
  2. Server Layer - HTTP API handling, routing, and event distribution
  3. Backend Core - Workspace management, session handling, and tool orchestration
  4. Data Layer - Database persistence and configuration management
  5. Integration Layer - LSP and MCP server connections

This modular structure enables:

  • Scalability - Multiple clients can connect to a single server instance
  • Extensibility - New providers and protocols can be added without core changes
  • Testability - Each layer can be tested in isolation
  • Persistence - Sessions survive client restarts

Source: https://github.com/charmbracelet/crush / Human Manual

Configuration Guide

Related topics: Getting Started with Crush, LLM Provider Setup, Permission System

Section Related Pages

Continue reading this section for the full explanation and source context.

Section Environment Variable Overrides

Continue reading this section for the full explanation and source context.

Section Ephemeral State Storage

Continue reading this section for the full explanation and source context.

Section Basic LSP Configuration

Continue reading this section for the full explanation and source context.

Related topics: Getting Started with Crush, LLM Provider Setup, Permission System

Configuration Guide

Crush is designed to work out-of-the-box with sensible defaults, but provides extensive configuration capabilities for customizing behavior, integrating with external tools, and adapting to specific workflows.

Configuration File Locations and Priority

Crush loads configuration from multiple locations with the following priority order (highest to lowest):

  1. .crush.json (project-local)
  2. crush.json (project root)
  3. $HOME/.config/crush/crush.json (user global)

Source: README.md:1

Environment Variable Overrides

You can override the default configuration and data directory locations using environment variables:

Environment VariablePurpose
CRUSH_GLOBAL_CONFIGOverride the global configuration file location
CRUSH_GLOBAL_DATAOverride the user data directory location
CRUSH_SKILLS_DIROverride the skills directory location

Source: README.md:1

Ephemeral State Storage

In addition to configuration, Crush stores ephemeral application state (such as session data) in:

  • Unix: $HOME/.local/share/crush/crush.json
  • Windows: %LOCALAPPDATA%\crush\crush.json

Source: README.md:1

Configuration File Structure

The configuration file is a JSON object with the following schema:

{
  "$schema": "https://charm.land/crush.json",
  "lsp": { ... },
  "mcp": { ... },
  "providers": { ... },
  "permissions": { ... },
  "hooks": { ... },
  "options": { ... },
  "commit": { ... }
}

Source: README.md:1

LSP Configuration

Crush integrates with Language Server Protocol (LSP) servers to provide additional context for code-aware decisions. LSPs are configured under the lsp key.

Basic LSP Configuration

{
  "$schema": "https://charm.land/crush.json",
  "lsp": {
    "go": {
      "command": "gopls",
      "env": {
        "GOTOOLCHAIN": "go1.24.5"
      }
    },
    "typescript": {
      "command": "typescript-language-server",
      "args": ["--stdio"]
    },
    "nix": {
      "command": "nil"
    }
  }
}

Source: README.md:1

LSP Configuration Options

FieldTypeDescription
commandstringThe LSP server executable
argsstring[]Command-line arguments
envobjectEnvironment variables for the LSP process
root_markersstring[]Files/directories that identify the project root
filetypesstring[]File extensions the LSP should handle

Backend LSP Management

The backend provides programmatic LSP management through the LSPManager:

// LSPStart starts an LSP server for the given path.
func (b *Backend) LSPStart(ctx context.Context, workspaceID, path string) error

// LSPStopAll stops all LSP servers for a workspace.
func (b *Backend) LSPStopAll(ctx context.Context, workspaceID string) error

// GetLSPDiagnostics retrieves diagnostics from an LSP client.
func (b *Backend) GetLSPDiagnostics(workspaceID, lspName string) (any, error)

Source: internal/backend/backend.go:1

MCP Configuration

Crush supports Model Context Protocol (MCP) servers for extending capabilities with external tools and resources.

Transport Types

MCP servers can be configured using three transport types:

TransportUse Case
stdioCommand-line servers that communicate via stdin/stdout
httpHTTP endpoints for web-based MCP servers
sseServer-Sent Events for streaming responses

MCP Configuration Example

{
  "mcp": {
    "servers": {
      "my-server": {
        "transport": "stdio",
        "command": "npx",
        "args": ["-y", "@some/mcp-server"]
      }
    }
  }
}

Shell Expansion in MCP Config

Shell-style value expansion works in command, args, env, headers, and url fields:

  • $VAR - Simple variable expansion
  • ${VAR:-default} - Default value if variable is unset
  • $(command) - Command substitution
  • Quoting and nesting are supported

This enables file-based secrets integration:

{
  "command": "$MCP_SERVER",
  "url": "$(cat /path/to/secret/token)"
}

Source: README.md:1

MCP Client API

The client provides methods for interacting with MCP servers:

// GetMCPServers retrieves all configured MCP servers for a workspace.
func (c *Client) GetMCPServers(ctx context.Context, id string) ([]MCPServer, error)

// GetMCPResources retrieves available resources from a server.
func (c *Client) GetMCPResources(ctx context.Context, id, serverID string) ([]MCPResourceContents, error)

// GetMCPPrompt retrieves a prompt from a named MCP server.
func (c *Client) GetMCPPrompt(ctx context.Context, id, clientID, promptID string, args map[string]string) (string, error)

Source: internal/client/config.go:1

Provider Configuration

Crush supports multiple LLM providers with various authentication methods.

Built-in Provider Environment Variables

Environment VariableProvider
ANTHROPIC_API_KEYAnthropic
OPENAI_API_KEYOpenAI
VERCEL_API_KEYVercel AI Gateway
GEMINI_API_KEYGoogle Gemini
SYNTHETIC_API_KEYSynthetic
ZAI_API_KEYZ.ai
MINIMAX_API_KEYMiniMax
HF_TOKENHugging Face Inference
CEREBRAS_API_KEYCerebras
OPENROUTER_API_KEYOpenRouter
IONET_API_KEYio.net
ALIBABA_SINGAPORE_API_KEYAlibaba (Singapore)
GROQ_API_KEYGroq
AVIAN_API_KEYAvian
OPENCODE_API_KEYOpenCode Zen & Go
VERTEXAI_PROJECTGoogle Cloud VertexAI
VERTEXAI_LOCATIONGoogle Cloud VertexAI
AWS_ACCESS_KEY_IDAmazon Bedrock (Claude)
AWS_SECRET_ACCESS_KEYAmazon Bedrock (Claude)
AWS_REGIONAmazon Bedrock (Claude)
AWS_PROFILEAmazon Bedrock (Custom Profile)
AWS_BEARER_TOKEN_BEDROCKAmazon Bedrock

Source: README.md:1

Custom Providers

#### OpenAI-Compatible APIs

For non-OpenAI providers with OpenAI-compatible APIs, use openai-compat:

{
  "$schema": "https://charm.land/crush.json",
  "providers": {
    "deepseek": {
      "type": "openai-compat",
      "base_url": "https://api.deepseek.com/v1",
      "api_key": "$DEEPSEEK_API_KEY"
    }
  }
}
[!NOTE]
Two provider types are supported for OpenAI-style APIs:
- openai - Use when proxying through OpenAI
- openai-compat - Use for non-OpenAI providers with OpenAI-compatible APIs

Source: README.md:1

Provider API Management

The client supports dynamic provider API key management:

// SetProviderAPIKey sets a provider API key on the server.
// The wire format tags the credential with an explicit Kind so the server
// can decode it back into the right Go type.
func (c *Client) SetProviderAPIKey(ctx context.Context, id string, scope config.Scope, providerID string, apiKey any) error

Source: internal/client/config.go:1

OAuth Providers

Crush supports OAuth-based authentication for providers like GitHub Copilot:

// ImportCopilot attempts to import a GitHub Copilot token on the server.
func (c *Client) ImportCopilot(ctx context.Context, id string) (*oauth.Token, bool, error)

// RefreshOAuthToken refreshes an OAuth token for a provider.
func (c *Client) RefreshOAuthToken(ctx context.Context, id string, scope config.Scope, providerID string) error

Source: internal/client/config.go:1

Permission Configuration

Crush implements a permission system for controlling tool execution. Permissions can be configured at different scopes.

Permission Scopes

ScopeDescription
globalApplied to all workspaces
workspaceApplied to a specific workspace
sessionApplied to a single session

Source: internal/backend/backend.go:1

Yolo Mode

When permissions.yolo is set to true, all tools are auto-approved without prompting. This is suitable for trusted environments:

{
  "permissions": {
    "yolo": true
  }
}

Permission Events

The system publishes permission events for auditing:

// PermissionRequest represents a request for tool execution permission
type PermissionRequest struct {
    ID          string
    SessionID   string
    ToolCallID  string
    ToolName    string
    Description string
    Action      string
    Path        string
    Params      map[string]any
}

// PermissionNotification represents a notification about a permission action
type PermissionNotification struct {
    ToolCallID string
    // ... additional fields
}

Source: internal/server/events.go:1

Hooks Configuration

Hooks allow you to run custom scripts at specific points in the agent workflow.

Hook Types

Crush supports hooks for various lifecycle events. Hooks use the built-in shell interpreter by default (since v0.67.0), improving cross-platform support.

Hooks Configuration

{
  "hooks": {
    "pre_tool_call": "/path/to/pre-hook.sh",
    "post_tool_call": "/path/to/post-hook.sh",
    "on_error": "/path/to/error-handler.sh"
  }
}

Source: docs/hooks/README.md

Hooks Use Built-in Shell

By default, Hooks will now use the built-in shell interpreter instead of the system shell. This particularly improves support on Windows and other platforms where bash may not be available.

Source: README.md:1 (v0.67.0 release notes)

Commit Configuration

Crush can assist with git commits with configurable attribution styles:

{
  "commit": {
    "trailer_style": "assisted-by",
    "generated_with": true
  }
}

Commit Configuration Options

OptionTypeDescription
trailer_stylestringAttribution trailer format
generated_withbooleanAdd "๐Ÿ’˜ Generated with Crush" line

Trailer Style Options

ValueFormat
assisted-byAssisted-by: Crush:[ModelID] (kernel convention)
co-authored-byCo-Authored-By: Crush <[email protected]>
noneNo attribution trailer

Source: README.md:1

Skills Configuration

Crush supports the Agent Skills open standard for extending capabilities.

Skills Search Paths

Skills are discovered from these locations (in order):

Global paths:

  • $CRUSH_SKILLS_DIR
  • $XDG_CONFIG_HOME/agents/skills or ~/.config/agents/skills/
  • $XDG_CONFIG_HOME/crush/skills or ~/.config/crush/skills/
  • ~/.agents/skills/
  • ~/.claude/skills/

Windows-specific:

  • %LOCALAPPDATA%\agents\skills\
  • %LOCALAPPDATA%\crush\skills\

Project-local paths:

  • .agents/skills
  • .crush/skills
  • .claude/skills
  • .cursor/skills

Custom Skills Paths

Add custom skills directories:

{
  "$schema": "https://charm.land/crush.json",
  "options": {
    "skills_paths": [
      "~/.config/crush/skills",
      "./project-skills"
    ]
  }
}

Skills Events

Skills discovery and loading are exposed via events:

// SkillsEvent represents skill discovery state changes
type SkillsEvent struct {
    States []SkillState
}

// SkillState represents the state of a discovered skill
type SkillState struct {
    Name  string
    Path  string
    State SkillDiscoveryState
    Err   error
}

Source: internal/server/events.go:1

Options Configuration

General Crush behavior can be customized through the options key.

Common Options

OptionTypeDescription
disabled_skillsstring[]List of skills to disable
skills_pathsstring[]Additional directories to search for skills
compactbooleanEnable compact UI mode

Example

{
  "$schema": "https://charm.land/crush.json",
  "options": {
    "disabled_skills": ["crush-config"]
  }
}

Compact Mode

Compact mode adjusts the UI to use less screen space, useful for terminal sessions with limited height.

// SetCompactMode sets compact mode on the server.
func (c *Client) SetCompactMode(ctx context.Context, id string, scope config.Scope, enabled bool) error

Source: internal/client/config.go:1

Server-Client Architecture

Crush supports an experimental server-client architecture (enabled with CRUSH_CLIENT_SERVER=1) where a persistent server handles workspace management and clients connect via HTTP.

Configuration in Server Mode

When running in server mode, the backend handles workspace-level configuration:

// NewServer creates a new Server with the given network and address.
func NewServer(cfg *config.ConfigStore, network, address string) *Server

// GetWorkspaceConfig returns the workspace-level configuration.
func (b *Backend) GetWorkspaceConfig(workspaceID string) (*config.Config, error)

// GetWorkspaceProviders returns the configured providers for a workspace.
func (b *Backend) GetWorkspaceProviders(workspaceID string) (any, error)

Source: internal/server/server.go:1 Source: internal/backend/backend.go:1

Dynamic Configuration Updates

Configuration can be modified at runtime through the server API:

// handlePostWorkspaceConfigSet sets a configuration field.
// POST /workspaces/{id}/config/set
func (c *controllerV1) handlePostWorkspaceConfigSet(w http.ResponseWriter, r *http.Request)

// handlePostWorkspaceConfigRemove removes a configuration field.
// POST /workspaces/{id}/config/remove
func (c *controllerV1) handlePostWorkspaceConfigRemove(w http.ResponseWriter, r *http.Request)

Source: internal/server/config.go:1

Configuration Architecture Diagram

graph TD
    A[Configuration Files] --> B[Load Priority]
    B --> C1[.crush.json]
    B --> C2[crush.json]
    B --> C3[~/.config/crush/crush.json]
    
    C1 --> D[Config Store]
    C2 --> D
    C3 --> D
    
    D --> E[Workspace Config]
    D --> F[Global Config]
    
    E --> G[Backend]
    F --> G
    
    G --> H[LSP Manager]
    G --> I[MCP Manager]
    G --> J[Permission System]
    G --> K[Provider Manager]
    
    L[Environment Variables] --> D
    L --> M[API Keys]
    L --> N[Overrides]
    
    H --> O[Language Servers]
    I --> P[MCP Servers]
    J --> Q[Tool Execution]
    K --> R[LLM Providers]

Common Configuration Issues

Global LSP Config Not Working

Users have reported issues with global LSP configuration not being applied. Ensure the configuration file is in the correct location and the JSON syntax is valid.

Known issue: LSP configuration in ~/.config/crush/crush.json may not be applied correctly in some versions. Source: GitHub Issue #2898

OpenRouter API Key Not Working

When using OpenRouter, ensure the API key is properly exported:

export OPENROUTER_API_KEY=sk-or-v1-...

The key should be set in the environment, not in the config file directly.

Source: GitHub Issue #2928

Database Corruption

The database stores session and workspace metadata. Corruption can occur from concurrent access or abrupt termination. The database is located at the data path defined by CRUSH_GLOBAL_DATA.

Source: GitHub Issue #2903

Built-in Configuration Skill

Crush ships with a builtin crush-config skill for configuring itself. You can simply ask Crush to configure settings:

Configure OpenRouter API key

This skill can be disabled if needed:

{
  "options": {
    "disabled_skills": ["crush-config"]
  }
}

Source: README.md:1

Testing Configuration

For testing purposes, the application provides a ShutdownForTest method that runs cleanup functions without triggering full production shutdown:

func (app *App) ShutdownForTest() {
    for _, cleanup := range app.cleanupFuncs {
        if cleanup != nil {
            _ = cleanup(context.Background())
        }
    }
    app.cleanupFuncs = nil
}

Source: internal/app/testing.go:1

Source: https://github.com/charmbracelet/crush / Human Manual

LLM Provider Setup

Related topics: Getting Started with Crush, Configuration Guide

Section Related Pages

Continue reading this section for the full explanation and source context.

Section Basic Provider Configuration

Continue reading this section for the full explanation and source context.

Section Setting API Keys via Environment Variables

Continue reading this section for the full explanation and source context.

Section Setting API Keys via Configuration

Continue reading this section for the full explanation and source context.

Related topics: Getting Started with Crush, Configuration Guide

LLM Provider Setup

Crush supports a wide variety of LLM providers, allowing users to choose their preferred AI models for coding assistance. This document covers how providers are configured, how to set up API keys, and how to add custom providers.

Overview

LLM providers in Crush are the bridges between the application and various AI model services. Each provider handles authentication, request formatting, and response parsing for specific API types. The provider system is designed to be extensible, supporting both built-in providers and custom configurations.

Source: README.md

Supported Built-in Providers

Crush ships with first-class support for numerous LLM providers. The following table summarizes the available providers and their authentication methods:

ProviderEnvironment VariableAPI Type
AnthropicANTHROPIC_API_KEYAnthropic
OpenAIOPENAI_API_KEYOpenAI
DeepSeekDEEPSEEK_API_KEYOpenAI-compatible
OpenRouterOPENROUTER_API_KEYOpenAI-compatible
Vercel AI GatewayVERCEL_API_KEYOpenAI-compatible
Google GeminiGEMINI_API_KEYOpenAI-compatible
HyperOAuth (Device Flow)Anthropic-compatible
GitHub CopilotOAuth (Token Import)OpenAI-compatible
SyntheticSYNTHETIC_API_KEYOpenAI-compatible
Z.aiZAI_API_KEYOpenAI-compatible
MiniMaxMINIMAX_API_KEYOpenAI-compatible
Hugging FaceHF_TOKENOpenAI-compatible
CerebrasCEREBRAS_API_KEYOpenAI-compatible
io.netIONET_API_KEYOpenAI-compatible
Alibaba (Singapore)ALIBABA_SINGAPORE_API_KEYOpenAI-compatible
GroqGROQ_API_KEYOpenAI-compatible
AvianAVIAN_API_KEYOpenAI-compatible
OpenCode Zen & GoOPENCODE_API_KEYOpenAI-compatible
Google VertexAIVERTEXAI_PROJECT, VERTEXAI_LOCATIONAnthropic-compatible
Amazon BedrockAWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGIONAnthropic-compatible

Source: README.md

Configuration Structure

Providers are configured in the crush.json file under the providers key. Configuration can be set at three levels with the following priority:

  1. .crush.json (project-local)
  2. crush.json (project root)
  3. $HOME/.config/crush/crush.json (global)

Basic Provider Configuration

{
  "providers": {
    "provider-name": {
      "type": "openai-compat",
      "base_url": "https://api.provider.com/v1",
      "api_key": "$PROVIDER_API_KEY",
      "models": {
        "large": ["model-id-1", "model-id-2"],
        "small": ["model-id-3"]
      }
    }
  }
}

Source: README.md

API Key Management

Setting API Keys via Environment Variables

The recommended approach for API key configuration is using environment variables. This keeps sensitive credentials out of configuration files:

export ANTHROPIC_API_KEY="sk-ant-..."
export OPENAI_API_KEY="sk-..."
export OPENROUTER_API_KEY="sk-or-..."

Setting API Keys via Configuration

API keys can also be set directly in the configuration file using string references:

{
  "providers": {
    "deepseek": {
      "type": "openai-compat",
      "base_url": "https://api.deepseek.com/v1",
      "api_key": "$DEEPSEEK_API_KEY"
    }
  }
}

Programmatic API Key Setting

The client provides methods to set provider API keys dynamically:

err := client.SetProviderAPIKey(ctx, workspaceID, config.ScopeGlobal, "openai", "api-key-string")

Source: internal/client/config.go:35-55

Custom Provider Setup

Crush supports two types of custom providers:

TypeUse Case
openaiWhen proxying or routing requests through OpenAI
openai-compatWhen using non-OpenAI providers with OpenAI-compatible APIs

OpenAI-Compatible Provider Example

Here's an example configuration for Deepseek:

{
  "$schema": "https://charm.land/crush.json",
  "providers": {
    "deepseek": {
      "type": "openai-compat",
      "base_url": "https://api.deepseek.com/v1",
      "api_key": "$DEEPSEEK_API_KEY",
      "models": {
        "large": ["deepseek-chat"],
        "small": ["deepseek-coder"]
      }
    }
  }
}

Source: README.md

Model String Parsing

Crush supports flexible model string formats. The model string can be specified in several ways:

FormatExampleDescription
Simple model namegpt-4oSearches all providers
Provider prefixopenai/gpt-4oUses specific provider
Nested model IDmoonshot/kimi-k2Model ID contains slashes
Full qualifiedsynthetic/moonshot/kimi-k2Provider with nested model ID

Parsing Logic

The parseModelStr function handles model string parsing:

func parseModelStr(providers map[string]config.ProviderConfig, modelStr string) (providerFilter, modelID string) {
    parts := strings.Split(modelStr, "/")
    if len(parts) == 1 {
        return "", parts[0]
    }
    // Check if the first part is a valid provider name
    if _, ok := providers[parts[0]]; ok {
        return parts[0], strings.Join(parts[1:], "/")
    }
    // First part is not a valid provider, treat entire string as model ID
    return "", modelStr
}

Source: internal/app/provider.go:16-32

Model Selection and Lookup

Finding Models Across Providers

The findModels function searches for models across configured providers:

func findModels(providers map[string]config.ProviderConfig, largeModel, smallModel string) ([]modelMatch, []modelMatch, error) {
    largeProviderFilter, largeModelID := parseModelStr(providers, largeModel)
    smallProviderFilter, smallModelID := parseModelStr(providers, smallModel)
    // Validation and matching logic...
}

Source: internal/app/provider.go:47-58

Model Selection Tests

The test suite validates various model string formats:

Test CaseInputExpected ProviderExpected Model ID
Simple modelgpt-4oopenaigpt-4o
Model with slashesmoonshot/kimi-k2syntheticmoonshot/kimi-k2
Provider and nested modelsynthetic/moonshot/kimi-k2syntheticmoonshot/kimi-k2
Invalid providernonexistent-provider/gpt-4oErrornot found

Source: internal/app/provider_test.go:18-45

OAuth-Based Providers

Some providers use OAuth authentication instead of API keys.

Hyper (Device Flow)

Hyper uses OAuth with device flow for authentication:

func (c *OAuthClient) Exchange(ctx context.Context, deviceCode,็ป‘็ป‘็ป‘็ป‘) (*Token, error)

Source: internal/oauth/hyper/device.go

GitHub Copilot (Token Import)

Copilot integration supports importing existing tokens:

func (c *Client) ImportCopilot(ctx context.Context, id string) (*oauth.Token, bool, error)

Source: internal/client/config.go:68-84

Provider API Endpoints

The backend exposes REST endpoints for provider management:

EndpointMethodPurpose
/workspaces/{id}/config/setPOSTSet a configuration field
/workspaces/{id}/config/providersGETGet workspace providers
/workspaces/{id}/config/import-copilotPOSTImport Copilot token

Source: internal/server/config.go:15-45

Provider Configuration Flow

graph TD
    A[Load crush.json] --> B{Config Level}
    B -->|.crush.json| C[Project Local]
    B -->|crush.json| D[Project Root]
    B -->|$HOME/.config/crush/crush.json| E[Global]
    C --> F[Merge Configs]
    D --> F
    E --> F
    F --> G[Resolve API Keys]
    G --> H[Validate Providers]
    H --> I[Provider Ready]

Troubleshooting

OpenRouter Authentication Issues

Users have reported issues with OpenRouter API key authentication:

"I been trying to use OR models by setting up and exporting a proper API key as OPENROUTER_API_KEY, but I'm still getting errors ERROR: Unauthorized"

Ensure that:

  1. The OPENROUTER_API_KEY environment variable is properly set
  2. The API key has sufficient permissions
  3. The provider configuration uses openai-compat type

Source: GitHub Issue #2928

Provider Not Found

If a model is not found, verify:

  1. The provider is properly configured in crush.json
  2. The provider name in the model string matches the configuration
  3. The model ID is valid for the provider

DeepSeek API Issues

A regression in v0.68.0 caused DeepSeek requests to return 400 (Bad Request). This was fixed in v0.69.0. If experiencing issues, ensure you're running the latest version.

Source: v0.69.0 Release Notes

See Also

Source: https://github.com/charmbracelet/crush / Human Manual

Built-in Tools

Related topics: Permission System, MCP Integration

Section Related Pages

Continue reading this section for the full explanation and source context.

Section File System Tools

Continue reading this section for the full explanation and source context.

Section Shell Execution Tools

Continue reading this section for the full explanation and source context.

Section Network Tools

Continue reading this section for the full explanation and source context.

Related topics: Permission System, MCP Integration

Built-in Tools

Crush provides a comprehensive suite of built-in tools that enable the AI agent to interact with the filesystem, execute shell commands, and fetch external resources. These tools form the foundational capabilities that allow the model to read, write, edit, search, and execute code within your development environment.

Overview

Built-in tools are registered capabilities exposed to the language model during agent execution. They provide controlled access to system operations with built-in permission checks, error handling, and security boundaries. The tool system is designed to be extensible, allowing the model to request tool executions with specific parameters while maintaining system safety through permission enforcement.

The tool architecture follows a unified interface pattern where each tool implements common interfaces for metadata, execution, and permission requirements. This design enables the agent coordinator to manage tool lifecycle, track usage, and enforce security policies consistently across all available tools.

graph TD
    A[Agent Coordinator] --> B[Tool Registry]
    B --> C[File Tools]
    B --> D[Shell Tools]
    B --> E[Network Tools]
    C --> C1[Read]
    C --> C2[Write]
    C --> C3[Edit]
    C --> C4[View]
    C --> C5[Grep]
    C --> C6[Glob]
    D --> D1[Bash]
    E --> E1[Fetch]
    E --> E2[Download]
    F[Permission Service] -.-> B
    G[Shell Executor] -.-> D1

Tool Categories

File System Tools

File system tools provide the agent with the ability to read, write, and modify files within the workspace. These tools handle various file operations including content reading, writing new files, editing existing files, viewing file contents with syntax highlighting, and searching for files by patterns.

Available File System Tools:

ToolPurpose
ReadRead file contents with optional line range specification
WriteWrite or overwrite entire file contents
EditApply targeted modifications to specific file sections
ViewDisplay file contents with formatting and line numbers
GrepSearch file contents using pattern matching
GlobFind files matching glob patterns

Shell Execution Tools

Shell execution tools enable the agent to run bash commands and scripts within the development environment. The bash tool provides direct shell access with configurable permissions and security boundaries.

Security Considerations:

The bash tool executes commands through the system's shell interpreter. Security restrictions are in place to prevent execution of potentially harmful commands. Users have requested the ability to remove these security limits for unrestricted terminal access in certain workflows. Source: internal/agent/tools/bash.go

The bash tool has been improved to handle interactive commands properly. A recent fix prevents the tool from hanging when the model attempts to run interactive commands like git rebase -i. Source: v0.74.1 Release Notes

Network Tools

Network tools allow the agent to fetch external resources and download files from URLs.

ToolPurpose
FetchRetrieve content from HTTP/HTTPS URLs
DownloadDownload files from URLs to local filesystem

Tool Interface

All built-in tools implement the Tool interface defined in the tools package. This interface ensures consistent behavior across all tool implementations and enables the agent system to manage tools uniformly.

Core Interface Components

Each tool provides:

  • Name: Unique identifier used in tool calls
  • Description: Human-readable explanation of tool purpose
  • Parameters: Schema defining required and optional inputs
  • Execute Function: Implementation logic for the tool operation

Source: internal/agent/tools/tools.go

Tool Registration

Tools are registered with the tool registry during application initialization. The registry maintains a mapping of tool names to tool instances and provides lookup functionality for the agent coordinator.

sequenceDiagram
    participant App as Application
    participant Registry as Tool Registry
    participant Tool as Individual Tool
    App->>Registry: Register Tool Instance
    Registry->>Tool: Initialize Tool
    Note over Registry: Store Tool in Map
    App->>Registry: Get Tool by Name
    Registry-->>App: Return Tool Instance

File Reading Tool

The Read tool provides controlled access to file contents. It supports reading specific line ranges and handles various file encodings.

Parameters:

ParameterTypeRequiredDescription
file_pathstringYesPath to the file to read
offsetintegerNoStarting line number (0-indexed)
limitintegerNoMaximum number of lines to read

Behavior:

  • Returns file contents with line numbers for easy reference
  • Supports partial reads via offset and limit parameters
  • Handles binary files gracefully with appropriate error messages

Source: internal/agent/tools/read.go

File Writing Tool

The Write tool creates new files or overwrites existing ones with provided content.

Parameters:

ParameterTypeRequiredDescription
file_pathstringYesDestination file path
contentstringYesContent to write to the file

Behavior:

  • Creates parent directories if they don't exist
  • Overwrites existing files completely
  • Returns confirmation with file path and character count

Source: internal/agent/tools/write.go

File Editing Tool

The Edit tool applies targeted modifications to existing files using a str_replace format that specifies exact locations for replacement.

Parameters:

ParameterTypeRequiredDescription
file_pathstringYesFile to edit
old_stringstringYesExact text to replace
new_stringstringYesReplacement text

Behavior:

  • Performs exact string matching for replacement
  • Requires precise old_string specification
  • Only replaces the first matching occurrence by default

Source: internal/agent/tools/edit.go

File Viewing Tool

The View tool displays file contents with formatting suitable for user display. It provides a more visually oriented view compared to the Read tool.

Parameters:

ParameterTypeRequiredDescription
file_pathstringYesPath to the file to view
offsetintegerNoStarting line number
limitintegerNoMaximum lines to display

Source: internal/agent/tools/view.go

Search Tools

Grep Tool

The Grep tool searches file contents for patterns matching specified criteria.

Parameters:

ParameterTypeRequiredDescription
patternstringYesRegular expression pattern
pathstringYesDirectory or file to search
file_patternstringNoFile glob pattern filter
include_hiddenbooleanNoInclude hidden files (default: false)

Behavior:

  • Uses regular expression matching for pattern searching
  • Optionally filters by file patterns
  • Returns matching lines with context

Source: internal/agent/tools/grep.go

Glob Tool

The Glob tool finds files matching glob patterns within the workspace.

Parameters:

ParameterTypeRequiredDescription
patternstringYesGlob pattern (e.g., **/*.go)
pathstringNoBase directory for search

Behavior:

  • Supports standard glob patterns including ** for recursive matching
  • Returns list of matching file paths
  • Searches within workspace boundaries

Source: internal/agent/tools/glob.go

Bash Tool

The Bash tool executes shell commands in the development environment. It provides direct access to system shell capabilities while maintaining permission controls.

Parameters:

ParameterTypeRequiredDescription
commandstringYesShell command to execute
timeoutintegerNoExecution timeout in seconds

Behavior:

  • Executes commands through the system shell (/bin/sh)
  • Captures stdout and stderr output
  • Returns exit code and execution duration
  • Commands run within the workspace directory context

Source: internal/agent/tools/bash.go

Security Model:

The bash tool operates under the configured permission mode. Users can configure permissions.yolo mode to auto-approve all bash commands without prompting, or use safe mode for explicit approval of each command execution.

Network Tools

Fetch Tool

The Fetch tool retrieves content from HTTP/HTTPS URLs.

Parameters:

ParameterTypeRequiredDescription
urlstringYesURL to fetch
headersobjectNoCustom HTTP headers

Behavior:

  • Supports GET requests by default
  • Can include custom headers for authentication
  • Returns response body and metadata

Source: internal/agent/tools/fetch.go

Download Tool

The Download tool fetches content from URLs and saves to the local filesystem.

Parameters:

ParameterTypeRequiredDescription
urlstringYesURL to download
file_pathstringYesDestination file path

Behavior:

  • Downloads content and writes to specified path
  • Creates parent directories as needed
  • Returns file metadata on success

Source: internal/agent/tools/download.go

Permission System

Built-in tools interact with Crush's permission system to enforce security boundaries. Each tool declares its permission requirements, and the permission service validates execution requests.

Permission Modes

ModeBehavior
safePrompts for confirmation before each tool execution
yoloAuto-approves all tool executions

Configuration Location: crush.json โ†’ permissions.yolo

Source: Configuration Documentation

Community Request: Users have requested the ability to switch between permission modes at runtime without restarting the session. This feature is tracked in GitHub Issue #2935.

Tool Execution Flow

sequenceDiagram
    participant Model as Language Model
    participant Coordinator as Agent Coordinator
    participant Registry as Tool Registry
    participant Tool as Tool Implementation
    participant Perms as Permission Service
    participant Shell as Shell Executor
    
    Model->>Coordinator: Request Tool Execution
    Coordinator->>Registry: Lookup Tool
    Registry-->>Coordinator: Return Tool Instance
    Coordinator->>Perms: Check Permission
    Perms-->>Coordinator: Approved/Denied
    Coordinator->>Tool: Execute with Params
    Tool->>Shell: Run System Operations
    Shell-->>Tool: Operation Result
    Tool-->>Coordinator: Tool Result
    Coordinator-->>Model: Return Result

Error Handling

Built-in tools implement consistent error handling patterns:

  • File Not Found: Returns descriptive error with path information
  • Permission Denied: Triggers permission request flow
  • Timeout: Returns partial results with timeout indicator
  • Invalid Parameters: Returns validation error with parameter details

All tool errors are wrapped with context information to aid debugging and logging.

Configuration

Tools can be configured through the Crush configuration file:

{
  "$schema": "https://charm.land/crush.json",
  "tools": {
    "bash": {
      "timeout": 60
    }
  }
}

See Also

Source: https://github.com/charmbracelet/crush / Human Manual

Permission System

Related topics: Built-in Tools, Configuration Guide

Section Related Pages

Continue reading this section for the full explanation and source context.

Section Components

Continue reading this section for the full explanation and source context.

Section Yolo Mode

Continue reading this section for the full explanation and source context.

Section Safe Mode (Default)

Continue reading this section for the full explanation and source context.

Related topics: Built-in Tools, Configuration Guide

Permission System

Overview

The Permission System in Crush provides a security layer that controls which tools and operations the AI agent can execute. It acts as an intermediary between the agent's intent and actual execution, requiring user confirmation for sensitive operations while providing flexibility through configurable modes.

The system is designed around a client-server architecture where permissions are managed per-workspace and can be controlled through both the HTTP API and configuration settings.

Architecture

The permission system follows a multi-layer architecture:

graph TD
    A[AI Agent] --> B[Tool Execution Request]
    B --> C{Permission Mode}
    C -->|Yolo| D[Auto-Approve]
    C -->|Safe| E[Request Permission]
    E --> F[SSE Broadcast]
    F --> G[Client A]
    F --> H[Client B]
    G --> I{User Decision}
    H --> I
    I -->|Allow| J[Execute Tool]
    I -->|Deny| K[Reject Tool]
    D --> J
    J --> L[Return Result]
    K --> L

Components

ComponentFileResponsibility
Permission Managerinternal/permission/permission.goCore permission logic and state management
Backend Handlersinternal/backend/permission.goWorkspace-level permission operations
API Controllersinternal/server/proto.goHTTP endpoint handlers for permission APIs
Client APIinternal/client/proto.goClient-side permission request methods
Event Broadcastinginternal/server/events.goSSE event distribution for permission notifications

Permission Modes

Crush supports two primary permission modes configured via crush.json:

Yolo Mode

In Yolo mode, all tool executions are automatically approved without user interaction. This is suitable for trusted environments or automated workflows.

{
  "permissions": {
    "yolo": true
  }
}

Safe Mode (Default)

In Safe mode, the system prompts for user confirmation before executing tools. Permission requests are broadcast to all connected clients via Server-Sent Events (SSE), allowing any client to grant or deny the request.

API Endpoints

The permission system exposes the following REST endpoints:

Set Skip Permissions

Sets whether permission prompts should be skipped for a workspace.

POST /workspaces/{id}/permissions/skip
ParameterTypeLocationDescription
idstringpathWorkspace ID
SkipboolbodyWhether to skip permission prompts

Response Codes:

CodeDescription
200Success
400Invalid request body
404Workspace not found
500Internal server error

Source: internal/server/proto.go:35-55

Get Skip Permissions Status

Retrieves the current skip-permissions setting for a workspace.

GET /workspaces/{id}/permissions/skip
ParameterTypeLocationDescription
idstringpathWorkspace ID

Response:

{
  "Skip": true
}

Source: internal/server/proto.go:77-91

Grant Permission

Grants or denies a pending permission request.

POST /workspaces/{id}/permissions/grant

Request Body:

{
  "permission": {
    "id": "req-123",
    "tool_call_id": "call-456",
    "tool_name": "view",
    "description": "read a file",
    "action": "read",
    "path": "/path/to/file"
  },
  "action": "allow"
}

Source: internal/server/proto.go:16-34

Backend Implementation

Workspace Permission Methods

The backend provides methods for managing workspace-level permission settings:

// SetPermissionsSkip sets whether permission prompts are skipped.
func (b *Backend) SetPermissionsSkip(workspaceID string, skip bool) error

// GetPermissionsSkip returns whether permission prompts are skipped.
func (b *Backend) GetPermissionsSkip(workspaceID string) (bool, error)

Source: internal/backend/permission.go:85-100

Permission Request Flow

When a tool execution requires permission:

  1. The agent initiates a permission request through App.Permissions.Request()
  2. The request is published to all connected SSE subscribers
  3. Any client can respond with a grant or denial
  4. The first grant resolves the pending request
granted, err := h.app.Permissions.Request(ctx, permission.CreatePermissionRequest{
    SessionID:   sessionID,
    ToolCallID:  toolCallID,
    ToolName:    "view",
    Description: "read a file",
    Action:      "read",
    Path:        h.workspace.Path,
})

Source: internal/server/e2e_test.go:180-190

Client SDK

The client provides methods for interacting with the permission system:

SetPermissionsSkipRequests

Enables or disables permission prompts for a workspace.

func (c *Client) SetPermissionsSkipRequests(ctx context.Context, id string, skip bool) error

Source: internal/client/proto.go:180-190

GetPermissionsSkipRequests

Retrieves the current permission skip setting.

func (c *Client) GetPermissionsSkipRequests(ctx context.Context, id string) (bool, error)

Source: internal/client/proto.go:192-208

Event System

Permission events are broadcast to all connected clients via SSE:

sequenceDiagram
    participant Agent
    participant Server
    participant ClientA
    participant ClientB
    
    Agent->>Server: Tool Execution Request
    Server->>ClientA: PermissionRequest Event
    Server->>ClientB: PermissionRequest Event
    ClientA->>Server: Grant Permission
    Server->>Agent: Resolution (resolved=true)
    Server->>ClientB: PermissionNotification Event

Event Types

Event TypePayloadDescription
PermissionRequestTool details, action, pathRequest for permission to execute
PermissionNotificationToolCallID, Granted, DeniedNotification of permission decision
PermissionGrantResolution statusResponse to permission request

Source: internal/server/events.go:60-70

Error Handling

The API handles errors by mapping them to appropriate HTTP status codes:

func (c *controllerV1) handleError(w http.ResponseWriter, r *http.Request, err error) {
    // A canceled agent run is not an error from the prompting
    // client's perspective. The cancellation reaches every SSE
    // subscriber via the FinishReasonCanceled marker on the assistant
    // message; the still-open POST should not surface a 500.
    if errors.Is(err, context.Canceled) {
        w.WriteHeader(http.StatusOK)
        return
    }
    status := http.StatusInternalServerError
    // ... error mapping logic
}

Source: internal/server/proto.go:93-105

Common Errors

ErrorConstantDescription
Workspace not foundErrWorkspaceNotFoundSpecified workspace does not exist
Invalid permission actionErrInvalidPermissionActionAction is not allow/deny

Source: internal/backend/backend.go:32-43

Configuration

Permissions can be configured at three levels with the following priority:

  1. .crush.json (project level)
  2. crush.json (project level)
  3. $HOME/.config/crush/crush.json (global)

Example configuration:

{
  "$schema": "https://charm.land/crush.json",
  "permissions": {
    "yolo": false
  }
}

Source: README.md

Community Considerations

Feature Request: Multiple Permission Profiles

Issue #2935 requests the ability to switch between permission profiles at runtime:

  • Yolo Mode: Auto-approve all tool calls
  • Safe Mode: Require confirmation for each tool call
  • Custom Mode: Per-tool permission rules

Currently, the permissions.yolo setting applies globally for the entire session and cannot be changed without restarting Crush.

Bash Command Security

Issue #928 discusses security restrictions on bash commands. Users have requested the ability to remove security limits for unrestricted terminal access, though this involves trade-offs with the permission system's safety guarantees.

Testing

The permission system includes end-to-end tests that verify:

  • Permission requests are properly broadcast to all clients
  • The first grant resolves pending requests with resolved=true
  • Subsequent grants do not affect already-resolved requests
  • Permission notifications are delivered to all clients
// First grant must report resolved=true
resolvedA := h.grantPermission(t, ctx, h.workspace.ID, proto.PermissionGrant{
    Permission: reqEv.Payload,
    Action:     proto.PermissionAllow,
})
require.True(t, resolvedA, "client A's grant must resolve the pending request")

Source: internal/server/e2e_test.go:198-204

Source: https://github.com/charmbracelet/crush / Human Manual

MCP Integration

Related topics: Built-in Tools, Skills System

Section Related Pages

Continue reading this section for the full explanation and source context.

Section Core Components

Continue reading this section for the full explanation and source context.

Section MCPClientInfo Data Model

Continue reading this section for the full explanation and source context.

Section Supported Event Types

Continue reading this section for the full explanation and source context.

Related topics: Built-in Tools, Skills System

MCP Integration

The Model Context Protocol (MCP) integration in Crush enables the AI agent to interact with external MCP-compatible servers, extending its capabilities through tools, prompts, and resources exposed by those servers. This integration allows Crush to serve as a unified interface for multiple MCP servers within a workspace, providing seamless access to external functionality.

Architecture Overview

The MCP integration follows a client-server architecture where Crush acts as a central hub managing connections to multiple MCP servers. The system is designed to handle concurrent MCP clients, each potentially exposing different tools, prompts, and resources.

graph TD
    A[Crush Workspace] --> B[Workspace MCP Manager]
    B --> C[MCP Client 1]
    B --> D[MCP Client 2]
    B --> N[... More Clients]
    C --> E[Tools]
    C --> F[Prompts]
    C --> G[Resources]
    D --> H[Tools]
    D --> I[Prompts]
    D --> J[Resources]

Core Components

ComponentPackagePurpose
MCP Managerinternal/agent/tools/mcpManages MCP client lifecycle and tool registration
Server Handlersinternal/server/config.goREST API endpoints for MCP operations
Backend Logicinternal/backend/config.goBusiness logic for MCP interactions
Client Interfaceinternal/client/config.goHTTP client for MCP server communication
Event Systeminternal/server/events.goReal-time MCP state event broadcasting

Source: internal/app/app.go:42-50

MCP Client States

Each MCP client maintains a state machine that tracks its connection lifecycle. The integration supports the following states:

stateDiagram-v2
    [*] --> Disconnected
    Disconnected --> Connecting
    Connecting --> Connected
    Connected --> Disconnecting
    Disconnecting --> Disconnected
    Connecting --> Error
    Connected --> Error
    Error --> Connecting

MCPClientInfo Data Model

FieldTypeDescription
NamestringUnique identifier for the MCP server
StateMCPStateCurrent connection state
ErrorstringLast error message if in error state
ToolCountintNumber of tools exposed by the server
PromptCountintNumber of prompts available
ResourceCountintNumber of resources accessible
ConnectedAttimestampWhen the connection was established

Source: internal/server/config.go:10-18

MCP Event System

The integration uses an event-driven architecture to broadcast MCP state changes and updates to subscribed clients. Events are published through the central pubsub broker.

Supported Event Types

Event TypeTrigger
MCPEventStateChangedClient connection state changed
MCPEventToolsListChangedAvailable tools updated
MCPEventPromptsListChangedAvailable prompts updated
MCPEventResourcesListChangedAvailable resources updated

Source: internal/server/events.go:120-131

The event conversion function maps internal MCP event types to protocol buffer equivalents:

func mcpEventTypeToProto(t mcp.EventType) proto.MCPEventType {
    switch t {
    case mcp.EventStateChanged:
        return proto.MCPEventStateChanged
    case mcp.EventToolsListChanged:
        return proto.MCPEventToolsListChanged
    // ... other mappings
    }
}

Source: internal/server/events.go:137-149

REST API Endpoints

The MCP functionality is exposed through a set of REST endpoints under the /workspaces/{id}/mcp path.

Endpoint Reference

MethodPathDescription
GET/workspaces/{id}/mcp/statesGet states of all MCP clients
POST/workspaces/{id}/mcp/refresh-toolsRefresh tools for a named server
POST/workspaces/{id}/mcp/refresh-promptsRefresh prompts for a named server
POST/workspaces/{id}/mcp/refresh-resourcesRefresh resources for a named server
POST/workspaces/{id}/mcp/get-promptRetrieve a prompt from a server
POST/workspaces/{id}/mcp/read-resourceRead a resource from a server
POST/workspaces/{id}/mcp/docker/enableEnable Docker MCP server
POST/workspaces/{id}/mcp/docker/disableDisable Docker MCP server

Source: internal/server/config.go:45-140

Get MCP Client States

func (c *controllerV1) handleGetWorkspaceMCPStates(w http.ResponseWriter, r *http.Request) {
    id := r.PathValue("id")
    states := c.backend.MCPGetStates(id)
    result := make(map[string]proto.MCPClientInfo, len(states))
    for k, v := range states {
        result[k] = proto.MCPClientInfo{
            Name:          v.Name,
            State:         proto.MCPState(v.State),
            Error:         v.Error,
            ToolCount:     v.Counts.Tools,
            PromptCount:   v.Counts.Prompts,
            ResourceCount: v.Counts.Resources,
            ConnectedAt:   v.ConnectedAt,
        }
    }
    jsonEncode(w, result)
}

Source: internal/server/config.go:6-25

Tools Integration

MCP tools are exposed to the AI agent through the command system. Each tool from an MCP server is registered as a callable command with its schema exposed.

Tool Refresh Flow

sequenceDiagram
    participant Client
    participant Server
    participant Backend
    participant MCPTools
    Client->>Server: POST /mcp/refresh-tools
    Server->>Backend: RefreshMCPTools(workspaceID, name)
    Backend->>MCPTools: RefreshTools(ctx, cfg, name)
    MCPTools-->>Backend: Updated tool list
    Backend-->>Server: Success
    Server-->>Client: 200 OK

Source: internal/backend/config.go:80-86

Backend Tool Refresh Implementation

// RefreshMCPTools refreshes the tools for a named MCP server.
func (b *Backend) RefreshMCPTools(ctx context.Context, workspaceID, name string) error {
    ws, err := b.GetWorkspace(workspaceID)
    if err != nil {
        return err
    }
    mcptools.RefreshTools(ctx, ws.Cfg, name)
    return nil
}

Source: internal/backend/config.go:80-86

Prompts Integration

MCP prompts allow external servers to define reusable prompt templates that can be invoked by the agent. Prompts can accept arguments defined in their schema.

Prompt Request Model

FieldTypeRequiredDescription
ClientIDstringYesThe MCP client name
PromptIDstringYesThe prompt identifier
Argsmap[string]stringNoPrompt arguments

Source: internal/client/config.go:1-15

Get Prompt Implementation

func (c *Client) GetMCPPrompt(ctx context.Context, id, clientID, promptID string, args map[string]string) (string, error) {
    rsp, err := c.post(ctx, fmt.Sprintf("/workspaces/%s/mcp/get-prompt", id), nil, jsonBody(struct {
        ClientID string            `json:"client_id"`
        PromptID string            `json:"prompt_id"`
        Args     map[string]string `json:"args"`
    }{ClientID: clientID, PromptID: promptID, Args: args}), http.Header{"Content-Type": []string{"application/json"}})
    // ... error handling
    return result.Prompt, nil
}

Source: internal/client/config.go:80-95

Prompt Command Building

Prompts are exposed as commands through the command system, enabling the AI agent to invoke them:

commands = append(commands, MCPPrompt{
    ID:          key,
    Title:       prompt.Title,
    Description: prompt.Description,
    PromptID:    prompt.Name,
    ClientID:    mcpName,
    Arguments:   args,
})

Source: internal/commands/commands.go:180-187

Resources Integration

MCP resources provide access to external data that can be read by the agent. Resources are identified by URIs and can return text or binary content.

Resource Contents Model

type MCPResourceContents struct {
    URI      string `json:"uri"`
    MIMEType string `json:"mime_type,omitempty"`
    Text     string `json:"text,omitempty"`
    Blob     []byte `json:"blob,omitempty"`
}

Source: internal/client/config.go:60-65

Read Resource Implementation

func (c *Client) ReadMCPResource(ctx context.Context, id, name, uri string) ([]MCPResourceContents, error) {
    // ... HTTP request setup
    var contents []MCPResourceContents
    if err := json.NewDecoder(rsp.Body).Decode(&contents); err != nil {
        return nil, fmt.Errorf("failed to decode MCP resource: %w", err)
    }
    return contents, nil
}

Source: internal/client/config.go:67-78

Server-side Resource Reading

func (c *controllerV1) handlePostWorkspaceMCPReadResource(w http.ResponseWriter, r *http.Request) {
    id := r.PathValue("id")
    var req proto.MCPReadResourceRequest
    if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
        // error handling
        return
    }
    contents, err := c.backend.ReadMCPResource(r.Context(), id, req.Name, req.URI)
    if err != nil {
        c.handleError(w, r, err)
        return
    }
    jsonEncode(w, contents)
}

Source: internal/server/config.go:103-118

Docker MCP Support

Crush includes built-in support for Docker-based MCP servers, allowing users to easily enable or disable Docker MCP integration for their workspace.

Docker MCP Operations

OperationEndpointDescription
EnablePOST /workspaces/{id}/mcp/docker/enableEnable Docker MCP server
DisablePOST /workspaces/{id}/mcp/docker/disableDisable Docker MCP server

Enable Docker MCP

func (c *Client) EnableDockerMCP(ctx context.Context, id string) error {
    rsp, err := c.post(ctx, fmt.Sprintf("/workspaces/%s/mcp/docker/enable", id), nil, nil, nil)
    if err != nil {
        return fmt.Errorf("failed to enable docker MCP: %w", err)
    }
    defer rsp.Body.Close()
    if rsp.StatusCode != http.StatusOK {
        return fmt.Errorf("failed to enable docker MCP: status code %d", rsp.StatusCode)
    }
    return nil
}

Source: internal/client/config.go:67-77

Client-Side Operations

The Client struct in internal/client/proto.go provides methods for managing MCP resources and prompts programmatically.

Refresh Operations Reference

MethodParametersDescription
MCPRefreshPromptsctx, id, nameRefresh available prompts
MCPRefreshResourcesctx, id, nameRefresh available resources
MCPGetStatesctx, idGet all MCP client states

Source: internal/client/proto.go:200-230

Refresh Prompts Implementation

func (c *Client) MCPRefreshPrompts(ctx context.Context, id, name string) error {
    rsp, err := c.post(ctx, fmt.Sprintf("/workspaces/%s/mcp/refresh-prompts", id), nil,
        jsonBody(struct {
            Name string `json:"name"`
        }{Name: name}),
        http.Header{"Content-Type": []string{"application/json"}})
    if err != nil {
        return fmt.Errorf("failed to refresh MCP prompts: %w", err)
    }
    defer rsp.Body.Close()
    if rsp.StatusCode != http.StatusOK {
        return fmt.Errorf("failed to refresh MCP prompts: status code %d", rsp.StatusCode)
    }
    return nil
}

Source: internal/client/proto.go:200-215

Event Subscription

The application subscribes to MCP events and re-publishes them through the central event broker, enabling UI components and other subsystems to react to MCP state changes.

Event Subscription Setup

func setupSubscriber[T any](
    ctx context.Context,
    wg *sync.WaitGroup,
    name string,
    subscriber func(context.Context) <-chan pubsub.Event[T],
    broker *pubsub.Broker[tea.Msg],
) {
    wg.Go(func() {
        subCh := subscriber(ctx)
        for {
            select {
            case event, ok := <-subCh:
                if !ok {
                    slog.Debug("Subscription channel closed", "name", name)
                    return
                }
                broker.Publish(pubsub.UpdatedEvent, tea.Msg(event))
            case <-ctx.Done():
                slog.Debug("Subscription cancelled", "name", name)
                return
            }
        }
    })
}

Source: internal/app/app.go:70-91

MCP Event Subscription Registration

setupSubscriber(ctx, app.serviceEventsWG, "mcp", mcp.SubscribeEvents, app.events)

Source: internal/app/app.go:44

Backend Event Handling

The backend provides workspace-specific event subscriptions for MCP operations.

SubscribeEvents Implementation

func (b *Backend) SubscribeEvents(ctx context.Context, workspaceID string) (<-chan pubsub.Event[tea.Msg], error) {
    ws, err := b.GetWorkspace(workspaceID)
    if err != nil {
        return nil, err
    }
    return ws.Events(ctx), nil
}

Source: internal/backend/events.go:16-24

Error Handling

MCP operations follow a consistent error handling pattern throughout the codebase:

Error TypeResponse CodeDescription
Invalid request body400 Bad RequestJSON decode failed
Workspace not found404 Not FoundWorkspace ID does not exist
Server error500 Internal Server ErrorBackend operation failed

Source: internal/server/config.go:26-36

Example error handling pattern:

if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
    c.server.logError(r, "Failed to decode request", "error", err)
    jsonError(w, http.StatusBadRequest, "failed to decode request")
    return
}

Source: internal/server/config.go:26-30

Data Flow Diagram

The following diagram illustrates the complete data flow for a typical MCP operation:

sequenceDiagram
    participant User as User/Agent
    participant UI as UI Layer
    participant Server as REST API Server
    participant Backend as Backend Logic
    participant MCPTools as MCP Tools Module
    participant MCPServer as External MCP Server

    User->>UI: Trigger MCP operation
    UI->>Server: HTTP Request
    Server->>Backend: Call backend method
    Backend->>MCPTools: Execute MCP operation
    MCPTools->>MCPServer: Send MCP protocol message
    MCPServer-->>MCPTools: MCP response
    MCPTools-->>Backend: Processed result
    Backend-->>Server: Response data
    Server-->>UI: JSON response
    UI-->>User: Display result

Testing Considerations

When testing MCP integration components, the test infrastructure provides utilities for creating mock workspaces with client state tracking:

func newTestWorkspace(t *testing.T, b *Backend, key string) (*Workspace, *atomic.Int32) {
    var shutdowns atomic.Int32
    ws := &Workspace{
        ID:           uuid.New().String(),
        Path:         key,
        resolvedPath: key,
        clients:      make(map[string]*clientState),
        shutdownFn:   func() { shutdowns.Add(1) },
    }
    b.mu.Lock()
    b.workspaces.Set(ws.ID, ws)
    b.pathIndex[key] = ws.ID
    b.mu.Unlock()
    return ws, &shutdowns
}

Source: internal/backend/backend_test.go:25-38

Source: https://github.com/charmbracelet/crush / Human Manual

Skills System

Related topics: MCP Integration, Configuration Guide

Section Related Pages

Continue reading this section for the full explanation and source context.

Section Core Components

Continue reading this section for the full explanation and source context.

Section Skills Manager

Continue reading this section for the full explanation and source context.

Section Skill Catalog

Continue reading this section for the full explanation and source context.

Related topics: MCP Integration, Configuration Guide

Skills System

Crush's Skills System provides a powerful extensibility mechanism that allows the agent to discover, load, and invoke reusable skill packages. Skills are self-contained instructional modules that inform Crush's behavior, provide specialized knowledge, or enable specific workflows. The system follows the open Agent Skills standard, making skills portable across compatible agents.

Overview

The Skills System serves as a bridge between Crush's core reasoning capabilities and domain-specific expertise. When a skill is invoked, its instructions are loaded into the conversation context, allowing the model to leverage specialized knowledge for tasks within that skill's scope.

Skills can be triggered in two ways:

  1. Automatic Discovery: Crush analyzes the current context and automatically activates relevant skills based on file types, project structure, or detected workflow patterns.
  2. Manual Invocation: Users can explicitly invoke skills through the commands palette using user:skill-name for global skills or project:skill-name for project-local skills.

Architecture

Core Components

graph TD
    A[Skills Manager] --> B[Skill Catalog]
    A --> C[Skill Loader]
    A --> D[Event System]
    
    B --> E[Global Paths]
    B --> F[Project Paths]
    B --> G[Custom Paths]
    
    C --> H[SKILL.md Parser]
    C --> I[YAML Frontmatter]
    C --> J[Embedding Generator]
    
    D --> K[pubsub Broker]
    K --> L[UI Notifications]
    K --> M[Server Events]

Skills Manager

The SkillsManager (defined in internal/skills/manager.go) is the central orchestrator responsible for:

  • Scanning configured paths for available skills
  • Maintaining the skill catalog with metadata
  • Handling skill discovery and state tracking
  • Publishing skill-related events to the application

Key Responsibilities:

ResponsibilityDescription
Path ResolutionResolves skill directories from multiple configured locations
DiscoveryDiscovers skills by scanning for SKILL.md files
Catalog ManagementMaintains an indexed catalog of available skills
State TrackingMonitors the discovery state of each skill
Event PublishingNotifies subscribers of skill discovery and state changes

Skill Catalog

The SkillCatalog (in internal/skills/catalog.go) provides indexing and lookup capabilities:

type SkillCatalog struct {
    skills    map[string]*Skill
    mu        sync.RWMutex
    rootPaths []string
}

The catalog supports:

  • Fast lookup by skill name
  • Filtering by discovery state
  • Path-based grouping
  • Thread-safe operations with sync.RWMutex

Skill Data Model

Skills are represented by the Skill struct with the following structure:

FieldTypeDescription
NamestringHuman-readable skill name
PathstringAbsolute path to the skill directory
StateSkillDiscoveryStateCurrent discovery state
ErrerrorError encountered during discovery
InstructionsstringParsed content from SKILL.md
MetadataSkillMetadataYAML frontmatter configuration

Discovery States:

StateValueDescription
StateUnknown0Initial state before discovery
StateDiscovering1Currently scanning the skill directory
StateDiscovered2Skill found and parsed successfully
StateError3Error occurred during discovery

Skill Events

The skill system publishes events through the pubsub broker, allowing other components to react to skill state changes:

type Event struct {
    Type  EventType
    States []SkillState
}

Event types include:

  • skills.EventUpdated: Published when skill states change
  • skills.EventDiscovered: Published when new skills are found

Skill Discovery Paths

Crush searches for skills in a hierarchical set of locations, enabling both system-wide and project-specific skill deployment.

Global Skill Paths

Skills are discovered from these global locations (in priority order):

PlatformPathEnvironment Variable
Unix$XDG_CONFIG_HOME/agents/skills-
Unix~/.config/agents/skills/-
Unix$XDG_CONFIG_HOME/crush/skills-
Unix~/.config/crush/skills/-
Unix~/.agents/skills/-
Unix~/.claude/skills/-
Windows%LOCALAPPDATA%\agents\skills\-
Windows%LOCALAPPDATA%\crush\skills\-
All-$CRUSH_SKILLS_DIR
Config-options.skills_paths

Project-Level Skill Paths

Crush also discovers skills within the current project directory:

Relative PathPrefixExample Invocation
.agents/skillsproject:project:my-skill
.crush/skillsproject:project:my-skill
.claude/skillsproject:project:my-skill
.cursor/skillsproject:project:my-skill

Skill File Structure

SKILL.md Format

Skills are defined by a SKILL.md file containing:

Source: https://github.com/charmbracelet/crush / Human Manual

Common Issues and Troubleshooting

Related topics: Getting Started with Crush, Configuration Guide, LLM Provider Setup

Section Related Pages

Continue reading this section for the full explanation and source context.

Section Database Corruption

Continue reading this section for the full explanation and source context.

Section Database Connection Failures

Continue reading this section for the full explanation and source context.

Section Global LSP Config Not Working

Continue reading this section for the full explanation and source context.

Related topics: Getting Started with Crush, Configuration Guide, LLM Provider Setup

Common Issues and Troubleshooting

This page documents known issues, common failure modes, and troubleshooting procedures for Crush. It covers database problems, LSP configuration issues, authentication failures, bash tool limitations, and workspace management problems. The content is derived from community-reported issues and verified against the codebase.

Understanding Crash Error Flows

Crush uses a structured event system to propagate errors across the client-server architecture. When errors occur, they flow through multiple layers before reaching the user.

graph TD
    A[User Action] --> B[Client Request]
    B --> C[Server Controller]
    C --> D[Backend Business Logic]
    D --> E[Database/LSP/Tools]
    E -->|Error| F[Event Publication]
    F --> G[pubsub Broker]
    G --> H[Client Event Handler]
    H --> I[User Error Display]
    
    E -->|Success| J[Response]
    J --> I

The event system handles various event types including LSP state changes, MCP events, permission requests, and error notifications. Source: internal/event/all.go

Database Issues

Database Corruption

Symptom: Error message ERROR failed to get session message appearing frequently (every hour or two).

Cause: The SQLite database used for session storage becomes corrupted, typically during write operations or when the process is terminated unexpectedly.

Affected Environments: Virtual machines, containers, and systems with network-attached storage are particularly susceptible.

Troubleshooting Steps:

``bash zpool status # For ZFS systems fsck /path/to/device # For ext4 and other filesystems ``

  1. Check filesystem health:

``bash ls -la ~/.local/share/crush/ ``

  1. Verify Crush data directory permissions:
  1. Check for concurrent Crush instances accessing the same database.

Solutions:

SolutionCommand/Action
Clear session historyDelete ~/.local/share/crush/crush.json
Rebuild databaseRestart Crush and allow automatic reconstruction
Check storage healthRun filesystem diagnostics

Source: internal/db/db.go

Database Connection Failures

Symptom: ERROR failed to get session message errors during normal operation.

The database connection is managed through internal/db/connect.go. Connection failures can occur when:

  1. The database file is locked by another process
  2. File permissions prevent read/write access
  3. The storage device reports I/O errors
// Database connection error handling pattern
if err != nil {
    return nil, fmt.Errorf("failed to connect: %w", err)
}

Source: internal/db/connect.go

LSP Configuration Issues

Global LSP Config Not Working

Symptom: LSP configured in ~/.config/crush/crush.json is not being picked up. The deno check command works from the terminal, but Crush cannot use the Deno LSP.

Common Causes:

CauseVerification
Wrong config file locationEnsure config is in ~/.config/crush/crush.json
Incorrect LSP definitionCheck JSON schema compliance
Missing root markersLSP needs valid markers to activate

Configuration Example:

{
  "lsp": {
    "deno": {
      "command": "deno",
      "args": ["lsp"],
      "root_markers": ["deno.lock", "deno.json"],
      "filetypes": ["js", "ts", "jsx", "tsx", "json"]
    }
  }
}

Troubleshooting:

  1. Verify the Deno executable is in your PATH
  2. Check that root markers exist in your project
  3. Enable debug logging to see LSP initialization

The LSP manager handles server lifecycle, diagnostics, and state changes through a pubsub-based event system.

Source: internal/lsp/manager.go

LSP State Monitoring

LSP events are published through the event system with state information:

type LSPEvent struct {
    Type            LSPEventType
    Name            string
    State           lsp.ServerState
    Error           error
    DiagnosticCount int
}

Source: internal/app/lsp_events.go

Authentication and API Key Issues

OpenRouter Authentication Failures

Symptom: ERROR: Unauthorized when using OpenRouter models despite having OPENROUTER_API_KEY set.

Troubleshooting Checklist:

StepAction
1Verify environment variable is set: echo $OPENROUTER_API_KEY
2Check API key is valid and has not expired
3Ensure the key has permissions for the specific model
4Verify base URL configuration in crush.json

Configuration:

{
  "providers": {
    "openrouter": {
      "type": "openai-compat",
      "base_url": "https://openrouter.ai/api/v1",
      "api_key": "$OPENROUTER_API_KEY"
    }
  }
}

DeepSeek API Errors (v0.68.0 Regression)

Issue: In version 0.68.0, all DeepSeek API requests returned HTTP 400 (Bad Request) errors.

Status: Fixed in v0.69.0.

If you encounter this issue, upgrade to v0.69.0 or later.

OAuth Token Refresh Failures

Symptom: Authentication fails after initial setup, particularly with Hyper or Copilot providers.

Cause: OAuth tokens expire and need refresh. Multiple Crush instances running simultaneously could cause token refresh race conditions.

Status: Fixed in v0.65.3 for multi-session scenarios.

Source: internal/client/config.go

Bash Tool Issues

Interactive Command Hanging

Symptom: The bash tool hangs when running interactive commands like git rebase -i.

Cause: The bash tool did not properly handle interactive terminal mode, causing the process to block indefinitely.

Status: Fixed in v0.74.1. The bash tool now properly handles interactive commands.

Source: internal/agent/tools/bash.go

Security Restrictions on Bash Commands

Symptom: Commands like curl, wget, ssh, sudo are blocked or fail to execute.

Context: This is by design for security reasons. The bash tool has built-in restrictions to prevent potentially harmful operations.

Workaround: For development and testing workflows requiring unrestricted access, consider:

  1. Using the shell tool directly in a terminal session
  2. Breaking complex operations into smaller, approved commands
  3. Creating wrapper scripts for frequently used restricted commands

Source: internal/agent/tools/bash.go

Workspace and Session Issues

Workspace Resolution Problems

Symptom: Crush cannot find or access the correct workspace directory.

Troubleshooting:

// Workspace resolution handles symlinks and non-existent paths
func resolveWorkspaceKey(key string) (string, error) {
    // Symlinks are evaluated
    real, err := filepath.EvalSymlinks(tmp)
    // Non-existent paths fall back to absolute path
}

Source: internal/backend/backend_test.go

Session Message Retrieval Failures

Symptom: Cannot retrieve session history, prompts queue incorrectly.

Common Causes:

CauseError MessageSolution
Database corruptionfailed to get session messageClear database, restart
Workspace shutdownSession not foundCreate new session
Invalid session IDErrInvalidSessionIDUse valid session ID format

Client-Server Architecture Issues

Context: The experimental client-server architecture can be enabled with CRUSH_CLIENT_SERVER=1.

Known Limitations:

  • Not recommended for daily use in experimental mode
  • Multiple concurrent instances may cause token refresh conflicts
  • Database locking can occur with shared storage

Source: internal/server/proto.go

Logging and Diagnostics

Accessing Crush Logs

Crush maintains structured logs for troubleshooting. Access logs using:

crush logs

Log Configuration

The logging system captures:

  • Server startup and shutdown events
  • Database operations
  • LSP initialization and diagnostics
  • MCP server events
  • Permission requests
  • Error conditions
// Log entries include structured fields for debugging
log.Error("operation failed", 
    "component", "lsp",
    "error", err.Error(),
    "workspace", ws.ID)

Source: internal/log/log.go

Source: internal/cmd/logs.go

Permission System Issues

Permission Profiles (Issue #2935)

Symptom: Cannot switch between Yolo (auto-approve) and Safe (prompt) modes without restarting Crush.

Current Behavior: The permissions.yolo setting in crush.json is applied globally for the entire session. Runtime switching between permission modes is not supported.

Workaround: Modify crush.json and restart Crush to change permission behavior.

Expected Future Behavior: Support for multiple permission profiles (Yolo, Safe, Custom) switchable at runtime via command.

Event System Reference

Event Types and Payloads

Event TypePayloadDescription
LSP State ChangedLSPEventLSP server state transitions
Diagnostics ChangedLSPEventFile diagnostics updated
MCP EventMCPEventMCP server notifications
Permission RequestPermissionRequestTool execution approval needed
Permission NotificationPermissionNotificationPermission state changes
Skills EventSkillsEventSkill discovery state updates

Source: internal/server/events.go

Performance Considerations

Long Sessions and Messages (v0.69.1)

Version 0.69.1 addressed performance issues with:

  • Long sessions
  • Long messages
  • Extended thinking blocks
  • Mouse selection
  • Scrolling operations

If you experience performance degradation with extended use, consider upgrading to the latest version.

Getting Help

If issues persist after trying the troubleshooting steps above:

  1. Check the GitHub Issues for similar reports
  2. Enable debug logging with CRUSH_DEBUG=1
  3. Collect logs using crush logs
  4. Include your Crush version: crush --version
  5. Report the issue with reproduction steps

Source: https://github.com/charmbracelet/crush / Human Manual

Sessions and History

Related topics: Common Issues and Troubleshooting, Getting Started with Crush

Section Related Pages

Continue reading this section for the full explanation and source context.

Section Key Components

Continue reading this section for the full explanation and source context.

Section Session Data Model

Continue reading this section for the full explanation and source context.

Section Session Lifecycle Operations

Continue reading this section for the full explanation and source context.

Related topics: Common Issues and Troubleshooting, Getting Started with Crush

Sessions and History

Crush maintains a comprehensive session and history system that tracks all interactions within a workspace. Sessions serve as the primary unit of organization for conversations, storing messages, history items, and metadata that enable context persistence across the agent's lifecycle.

Architecture Overview

The session system follows a layered architecture with clear separation between the backend business logic, server transport layer, and client API.

graph TD
    Client["Client API<br/>internal/client/proto.go"] --> Server["HTTP Server<br/>internal/server/proto.go"]
    Server --> Backend["Backend<br/>internal/backend/session.go"]
    Backend --> Workspace["Workspace<br/>internal/workspace/workspace.go"]
    Workspace --> Sessions["Sessions Manager"]
    Workspace --> Messages["Messages Store"]
    Workspace --> History["History Store"]
    
    Sessions --> DB["SQLite Database<br/>internal/db/sessions.sql.go"]
    Messages --> DB
    History --> DB

Key Components

ComponentFileResponsibility
Backend Session APIinternal/backend/session.goBusiness logic for session operations
HTTP Handlersinternal/server/proto.goREST endpoint handlers
Client APIinternal/client/proto.goClient-side session methods
Database Layerinternal/db/sessions.sql.goPersistent storage for sessions

Sessions

Sessions represent individual conversation contexts within a workspace. Each session maintains its own message history and state, enabling users to switch between different work contexts.

Session Data Model

Sessions contain the following core attributes:

  • ID: Unique identifier for the session
  • Title: Human-readable session name
  • Created/Updated timestamps: Session lifecycle tracking
  • Messages: All messages exchanged within the session
  • History: Chronological record of actions and events

Session Lifecycle Operations

#### Creating Sessions

New sessions are created through the client API or directly via the backend:

// Client-side: internal/client/proto.go
func (c *Client) CreateSession(ctx context.Context, id string, title string) (*proto.Session, error)
// Backend: internal/backend/session.go
func (b *Backend) CreateSession(ctx context.Context, workspaceID string, title string) (session.Session, error)

The server validates the workspace exists and assigns a unique session ID before persisting to the database.

#### Listing Sessions

Retrieve all sessions within a workspace:

// Client API
func (c *Client) ListSessions(ctx context.Context, id string) ([]proto.Session, error)

// Backend implementation
func (b *Backend) ListSessions(ctx context.Context, workspaceID string) ([]session.Session, error)

Source: internal/backend/session.go:24-36

#### Getting a Specific Session

Retrieve a single session by ID:

func (b *Backend) GetSession(ctx context.Context, workspaceID, sessionID string) (session.Session, error)

Source: internal/backend/session.go:13-22

#### Saving/Updating Sessions

Session updates are persisted through the backend:

// internal/backend/session.go
func (b *Backend) SaveSession(ctx context.Context, workspaceID string, sess session.Session) (session.Session, error)

Source: internal/backend/session.go:47-54

#### Deleting Sessions

Sessions can be permanently removed:

// internal/backend/session.go
func (b *Backend) DeleteSession(ctx context.Context, workspaceID, sessionID string) error

Source: internal/backend/session.go:56-62

HTTP API Endpoints

The server exposes session operations via REST endpoints:

MethodEndpointHandlerDescription
GET/workspaces/{id}/sessionshandleGetWorkspaceSessionsList all sessions
POST/workspaces/{id}/sessionshandlePostWorkspaceSessionsCreate new session
GET/workspaces/{id}/sessions/{sid}handleGetWorkspaceSessionGet specific session
DELETE/workspaces/{id}/sessions/{sid}handleDeleteWorkspaceSessionDelete session

Source: internal/server/proto.go

Messages

Messages are the core content units within a session, representing individual exchanges between the user and the agent.

Message Types

The system supports multiple message content types through discriminated unions:

TypeDescription
TextContentPlain text or markdown content
ImageContentInline images with URL and detail
BinaryContentBinary data with MIME type and path

Source: internal/server/events.go:89-100

Message Retrieval Operations

#### List All Session Messages

Retrieve every message in a session:

// Client API
func (c *Client) ListMessages(ctx context.Context, id string, sessionID string) ([]proto.Message, error)

// Backend
func (b *Backend) ListSessionMessages(ctx context.Context, workspaceID, sessionID string) ([]message.Message, error)

Source: internal/backend/session.go:24-36

#### List User Messages Only

Filter to retrieve only user-role messages:

// Client API
func (c *Client) ListUserMessages(ctx context.Context, id string, sessionID string) ([]proto.Message, error)

// Backend
func (b *Backend) ListUserMessages(ctx context.Context, workspaceID, sessionID string) ([]message.Message, error)

Source: internal/backend/session.go:64-74

#### List All User Messages Across Sessions

Retrieve user messages from all sessions in a workspace:

// Client API
func (c *Client) ListAllUserMessages(ctx context.Context, id string) ([]proto.Message, error)

// Backend
func (b *Backend) ListAllUserMessages(ctx context.Context, workspaceID string) ([]message.Message, error)

Source: internal/backend/session.go:76-85

HTTP API Endpoints

MethodEndpointDescription
GET/workspaces/{id}/sessions/{sid}/messagesGet all messages for session
GET/workspaces/{id}/sessions/{sid}/messages/userGet user messages only
GET/workspaces/{id}/messages/userGet all user messages across sessions

Source: internal/server/proto.go

History

The history system tracks a chronological record of actions and events within each session, providing an audit trail and enabling features like session resumption.

History Items

History items record significant events during agent execution:

  • Tool invocations and results
  • File system changes
  • Command executions
  • State transitions

Listing Session History

// internal/backend/session.go
func (b *Backend) ListSessionHistory(ctx context.Context, workspaceID, sessionID string) (any, error) {
    ws, err := b.GetWorkspace(workspaceID)
    if err != nil {
        return nil, err
    }
    return ws.History.ListBySession(ctx, sessionID)
}

Source: internal/backend/session.go:38-45

Session History Files

The client can retrieve files associated with history items:

// internal/client/proto.go
func (c *Client) ListSessionHistoryFiles(ctx context.Context, id string, sessionID string) ([]proto.File, error)

This endpoint returns files read or modified during the session, useful for understanding what resources were accessed.

Source: internal/client/proto.go:24-37

File Tracker Integration

Sessions track files accessed during execution through the file tracker system:

// internal/server/proto.go
func (c *controllerV1) handleGetWorkspaceSessionFileTrackerFiles(w http.ResponseWriter, r *http.Request)
EndpointDescription
GET /workspaces/{id}/sessions/{sid}/filetracker/filesList files read in a session

Source: internal/server/proto.go

Event System

Sessions emit events for real-time updates, enabling the client to track session state changes and agent activity.

Event Types

Event TypePayloadDescription
AgentEventSessionID, SessionTitle, TypeAgent state changes
RunCompleteSessionID, RunID, MessageID, Text, Error, CancelledRun completion
PermissionRequestToolCallID, ToolName, Description, ActionPermission requests
PermissionNotificationToolCallID, ToolName, StatePermission state changes
LSPEventType, Name, State, Error, DiagnosticCountLSP server events
MCPEventType, Name, State, Error, ToolCountMCP server events
SkillsEventStates arraySkill discovery states
ConfigChangedConfig dataConfiguration updates

Source: internal/server/events.go

Event Conversion to Proto

The server converts internal events to protocol buffer format for transmission:

// internal/server/events.go
func messageToProto(m message.Message) proto.Message {
    msg := proto.Message{
        ID:        m.ID,
        Role:      proto.Role(m.Role),
        CreatedAt: m.CreatedAt.UnixMilli(),
    }
    // Content conversion...
    return msg
}

func messagesToProto(msgs []message.Message) []proto.Message {
    out := make([]proto.Message, len(msgs))
    for i, m := range msgs {
        out[i] = messageToProto(m)
    }
    return out
}

Source: internal/server/events.go:145-170

Workspace Context

Sessions are always scoped to a workspace, providing filesystem context and configuration.

Workspace-Session Relationship

graph LR
    Workspace["Workspace<br/>internal/workspace/workspace.go"] --> Sessions["Sessions Store"]
    Workspace --> Messages["Messages Store"]
    Workspace --> History["History Store"]
    Workspace --> Config["Config Store"]
    
    Sessions --> Session1["Session 1"]
    Sessions --> Session2["Session 2"]
    Sessions --> SessionN["Session N"]

Workspace Operations

MethodDescription
GetWorkspace(id)Retrieve workspace by ID
GetWorkspaceProto(id)Get workspace as proto representation
VersionInfo()Server version information
Config()Server-level configuration

Source: internal/backend/backend.go:57-100

Backend Core Operations

The backend manages session lifecycle through several key operations:

// Workspace key resolution
func resolveWorkspaceKey(path string) (string, error) {
    abs, err := filepath.Abs(path)
    if err != nil {
        return "", err
    }
    if resolved, err := filepath.EvalSymlinks(abs); err == nil {
        return resolved, nil
    }
    return abs, nil
}

Source: internal/backend/backend.go:157-168

Client Registration and Hold Mechanism

Sessions maintain a hold system to prevent premature cleanup:

// Test verification: internal/backend/backend_test.go
func TestReleaseHold_NoStreams(t *testing.T) {
    b, _ := newTestBackend(t)
    ws, shutdowns := insertTestWorkspace(t, b, "/tmp/a")
    
    cid := newClientID(t)
    b.registerClient(ws, cid)
    require.NoError(t, b.releaseHold(ws.ID, cid))
    
    require.Equal(t, int32(1), shutdowns.Load())
}

Source: internal/backend/backend_test.go:95-106

Session Summarization

When context windows approach capacity, sessions can be summarized to preserve essential information:

// Client API
func (c *Client) SummarizeSession(ctx context.Context, id, sessionID string) error

// Server handler
func (c *controllerV1) handlePostWorkspaceSessionSummarize(...)

Summarization preserves:

  • Key decisions and outcomes
  • Important file modifications
  • Task completion status

This feature directly addresses context window management concerns mentioned in v0.66.0 release notes about summarization fixes.

Error Handling

The backend defines specific errors for session operations:

ErrorDescription
ErrWorkspaceNotFoundRequested workspace does not exist
ErrClientNotAttachedClient not attached to workspace
ErrInvalidClientIDClient ID validation failed

Source: internal/backend/backend.go:32-41

Testing

The backend includes comprehensive test coverage for session management:

// internal/backend/backend_test.go
func TestHoldExpiry_TearsDown(t *testing.T) {
    b, srvShutdowns := newTestBackend(t)
    ws, wsShutdowns := insertTestWorkspace(t, b, "/tmp/a")
    
    cid := newClientID(t)
    b.registerClient(ws, cid)
    
    require.Eventually(t, func() bool {
        return wsShutdowns.Load() == 1 && srvShutdowns.Load() == 1
    }, 1*time.Second, 5*time.Millisecond)
}

Source: internal/backend/backend_test.go:83-93

Test Utilities

The app package provides test-specific shutdown handling:

// internal/app/testing.go
func (app *App) ShutdownForTest() {
    for _, cleanup := range app.cleanupFuncs {
        if cleanup != nil {
            _ = cleanup(context.Background())
        }
    }
    app.cleanupFuncs = nil
}

Source: internal/app/testing.go

  • Workspaces: Sessions exist within workspace context; see workspace documentation for lifecycle management
  • Messages: Detailed message content types and formatting
  • Events: Real-time event subscription and handling
  • Configuration: Session-specific configuration options

Source: https://github.com/charmbracelet/crush / Human Manual

Doramagic Pitfall Log

Source-linked risks stay visible on the manual page so the preview does not read like a recommendation.

medium Installation risk requires verification

Developers may fail before the first successful local run: Status line obscured by onboarding dialog

medium Installation risk requires verification

May increase setup, validation, or first-run risk for the user.

medium Configuration risk requires verification

Developers may misconfigure credentials, environment, or host setup: Qwen 3.7 Max on OpenCode Go Unauthorized Error

medium Configuration risk requires verification

Upgrade or migration may change expected behavior: v0.68.0

Doramagic Pitfall Log

Found 27 structured pitfall item(s), including 0 high/blocking item(s). Top priority: Installation risk - Installation risk requires verification.

1. Installation risk: Installation risk requires verification

  • Severity: medium
  • Finding: Developers should check this installation risk before relying on the project: Status line obscured by onboarding dialog
  • User impact: Developers may fail before the first successful local run: Status line obscured by onboarding dialog
  • Recommended check: Before packaging this project, run the relevant install/config/quickstart check for: Status line obscured by onboarding dialog. Context: Observed when using macos
  • Evidence: failure_mode_cluster:github_issue | fmev_a0f07b57c88e616e55f08e80117c394f | https://github.com/charmbracelet/crush/issues/3044

2. Installation risk: Installation risk requires verification

  • Severity: medium
  • Finding: Project evidence flags a installation risk. Review the linked source before relying on this workflow.
  • User impact: May increase setup, validation, or first-run risk for the user.
  • Recommended check: Reproduce the official install and quickstart path in an isolated environment.
  • Evidence: community_evidence:github | cevd_e98d3521bdb84c1398a9b2bbf776e5ae | https://github.com/charmbracelet/crush/issues/3044

3. Configuration risk: Configuration risk requires verification

  • Severity: medium
  • Finding: Developers should check this configuration risk before relying on the project: Qwen 3.7 Max on OpenCode Go Unauthorized Error
  • User impact: Developers may misconfigure credentials, environment, or host setup: Qwen 3.7 Max on OpenCode Go Unauthorized Error
  • Recommended check: Before packaging this project, run the relevant install/config/quickstart check for: Qwen 3.7 Max on OpenCode Go Unauthorized Error. Context: Observed when using linux
  • Evidence: failure_mode_cluster:github_issue | fmev_9e6004420beb68218b1e13d4bcf43db6 | https://github.com/charmbracelet/crush/issues/3027

4. Configuration risk: Configuration risk requires verification

  • Severity: medium
  • Finding: Developers should check this configuration risk before relying on the project: v0.68.0
  • User impact: Upgrade or migration may change expected behavior: v0.68.0
  • Recommended check: Before packaging this project, run the relevant install/config/quickstart check for: v0.68.0. Context: Source discussion did not expose a precise runtime context.
  • Evidence: failure_mode_cluster:github_release | fmev_bbaee295de66f70f36b7dc8593a9b7bd | https://github.com/charmbracelet/crush/releases/tag/v0.68.0

5. Configuration risk: Configuration risk requires verification

  • Severity: medium
  • Finding: Developers should check this configuration risk before relying on the project: v0.70.0
  • User impact: Upgrade or migration may change expected behavior: v0.70.0
  • Recommended check: Before packaging this project, run the relevant install/config/quickstart check for: v0.70.0. Context: Source discussion did not expose a precise runtime context.
  • Evidence: failure_mode_cluster:github_release | fmev_13d76ec440a4dbc5cec24de4d067e76e | https://github.com/charmbracelet/crush/releases/tag/v0.70.0

6. Configuration risk: Configuration risk requires verification

  • Severity: medium
  • Finding: Developers should check this configuration risk before relying on the project: v0.71.0
  • User impact: Upgrade or migration may change expected behavior: v0.71.0
  • Recommended check: Before packaging this project, run the relevant install/config/quickstart check for: v0.71.0. Context: Observed when using python
  • Evidence: failure_mode_cluster:github_release | fmev_8c04dff267efacde979bed34b0d877cd | https://github.com/charmbracelet/crush/releases/tag/v0.71.0

7. Configuration risk: Configuration risk requires verification

  • Severity: medium
  • Finding: Developers should check this configuration risk before relying on the project: v0.74.0
  • User impact: Upgrade or migration may change expected behavior: v0.74.0
  • Recommended check: Before packaging this project, run the relevant install/config/quickstart check for: v0.74.0. Context: Observed when using windows
  • Evidence: failure_mode_cluster:github_release | fmev_a06bc501b775f717bcff8195feac4cd4 | https://github.com/charmbracelet/crush/releases/tag/v0.74.0

8. Configuration risk: Configuration risk requires verification

  • Severity: medium
  • Finding: Project evidence flags a configuration risk. Review the linked source before relying on this workflow.
  • User impact: May increase setup, validation, or first-run risk for the user.
  • Recommended check: Reproduce the official install and quickstart path in an isolated environment.
  • Evidence: community_evidence:github | cevd_fbc15acd4ea14549b9798314e4d6b7df | https://github.com/charmbracelet/crush/issues/3045

9. Configuration risk: Configuration risk requires verification

  • Severity: medium
  • Finding: Project evidence flags a configuration risk. Review the linked source before relying on this workflow.
  • User impact: May increase setup, validation, or first-run risk for the user.
  • Recommended check: Reproduce the official install and quickstart path in an isolated environment.
  • Evidence: community_evidence:github | cevd_c8619fe17ed64827934419fb0b4880c7 | https://github.com/charmbracelet/crush/issues/3046

10. Configuration risk: Configuration risk requires verification

  • Severity: medium
  • Finding: Project evidence flags a configuration risk. Review the linked source before relying on this workflow.
  • User impact: May increase setup, validation, or first-run risk for the user.
  • Recommended check: Reproduce the official install and quickstart path in an isolated environment.
  • Evidence: community_evidence:github | cevd_bc68f6499e6d498fa2fcd70a2c6bdf8f | https://github.com/charmbracelet/crush/issues/3024

11. Capability evidence risk: Capability evidence risk requires verification

  • Severity: medium
  • Finding: README/documentation is current enough for a first validation pass.
  • User impact: May increase setup, validation, or first-run risk for the user.
  • Recommended check: Reproduce the official install and quickstart path in an isolated environment.
  • Evidence: capability.assumptions | github_repo:987670088 | https://github.com/charmbracelet/crush

12. Maintenance risk: Maintenance risk requires verification

  • Severity: medium
  • Finding: Project evidence flags a maintenance risk. Review the linked source before relying on this workflow.
  • User impact: May increase setup, validation, or first-run risk for the user.
  • Recommended check: Reproduce the official install and quickstart path in an isolated environment.
  • Evidence: evidence.maintainer_signals | github_repo:987670088 | https://github.com/charmbracelet/crush

Source: Doramagic discovery, validation, and Project Pack records

Community Discussion Evidence

These external discussion links are review inputs, not standalone proof that the project is production-ready.

Sources 11

Count of project-level external discussion links exposed on this manual page.

Use Review before install

Open the linked issues or discussions before treating the pack as ready for your environment.

Community Discussion Evidence

Doramagic exposes project-level community discussion separately from official documentation. Review these links before using crush with real data or production workflows.

Source: Project Pack community evidence and pitfall evidence