SKILL.md


name: crush-config description: Use when the user needs help configuring Crush — working with crush.json, setting up providers, configuring LSPs, adding MCP servers, managing skills or permissions, or changing Crush behavior.

Crush Configuration

Crush uses JSON configuration files with the following priority (highest to lowest):

  1. .crush.json (project-local, hidden)
  2. crush.json (project-local)
  3. $XDG_CONFIG_HOME/crush/crush.json or $HOME/.config/crush/crush.json (global)

Basic Structure

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

The $schema property enables IDE autocomplete but is optional.

Common Tasks

  • Add a custom provider: add an entry under providers with type, base_url, api_key, and models.
  • Disable a builtin or local skill: add the skill name to options.disabled_skills.
  • Add an MCP server: add an entry under mcp with type and either command (stdio) or url (http/sse).

Model Selection

{
  "models": {
    "large": {
      "model": "claude-sonnet-4-20250514",
      "provider": "anthropic",
      "max_tokens": 16384
    },
    "small": {
      "model": "claude-haiku-4-20250514",
      "provider": "anthropic"
    }
  }
}
  • large is the primary coding model; small is for summarization.
  • Only model and provider are required.
  • Optional tuning: reasoning_effort, think, max_tokens, temperature, top_p, top_k, frequency_penalty, presence_penalty, provider_options.

Custom Providers

{
  "providers": {
    "deepseek": {
      "type": "openai-compat",
      "base_url": "https://api.deepseek.com/v1",
      "api_key": "$DEEPSEEK_API_KEY",
      "models": [
        {
          "id": "deepseek-chat",
          "name": "Deepseek V3",
          "context_window": 64000
        }
      ]
    }
  }
}
  • type (required): openai, openai-compat, or anthropic
  • api_key supports $ENV_VAR syntax.
  • Additional fields: disable, system_prompt_prefix, extra_headers, extra_body, provider_options.

LSP Configuration

{
  "lsp": {
    "go": {
      "command": "gopls",
      "env": { "GOTOOLCHAIN": "go1.24.5" }
    },
    "typescript": {
      "command": "typescript-language-server",
      "args": ["--stdio"]
    }
  }
}
  • command (required), args, env cover most setups.
  • Additional fields: disabled, filetypes, root_markers, init_options, options, timeout.

MCP Servers

{
  "mcp": {
    "filesystem": {
      "type": "stdio",
      "command": "node",
      "args": ["/path/to/mcp-server.js"]
    },
    "github": {
      "type": "http",
      "url": "https://api.githubcopilot.com/mcp/",
      "headers": {
        "Authorization": "Bearer $GH_PAT"
      }
    }
  }
}
  • type (required): stdio, sse, or http
  • Additional fields: env, disabled, disabled_tools, timeout.

Options

{
  "options": {
    "skills_paths": ["./skills"],
    "disabled_tools": ["bash", "sourcegraph"],
    "disabled_skills": ["crush-config"],
    "tui": {
      "compact_mode": false,
      "diff_mode": "unified",
      "transparent": false
    },
    "auto_lsp": true,
    "debug": false,
    "debug_lsp": false,
    "attribution": {
      "trailer_style": "assisted-by",
      "generated_with": true
    }
  }
}

[!IMPORTANT] The following skill paths are loaded by default and DO NOT NEED to be added to skills_paths: .agents/skills, .crush/skills, .claude/skills, .cursor/skills

Other options: context_paths, progress, disable_notifications, disable_auto_summarize, disable_metrics, disable_provider_auto_update, disable_default_providers, data_directory, initialize_as.

Hooks

Hooks are user-defined shell commands that fire on agent events. Currently only PreToolUse is supported, which runs before a tool is executed.

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "^(edit|write|multiedit)$",
        "command": ".crush/hooks/protect-files.sh"
      },
      {
        "matcher": "^bash$",
        "command": ".crush/hooks/no-haskell.sh"
      }
    ]
  }
}

Hook Properties

  • command (required): Shell command to execute. Runs via sh -c.
  • matcher (optional): Regex pattern tested against the tool name. Empty or absent means match all tools.
  • timeout (optional): Timeout in seconds. Defaults to 30.

Event Name Normalization

Event names are case-insensitive and accept snake_case variants: PreToolUse, pretooluse, pre_tool_use, and PRE_TOOL_USE all work.

How Hooks Work

  1. When a tool is about to be called, all PreToolUse hooks with a matching matcher (or no matcher) run in parallel.
  2. Duplicate commands are deduplicated — each unique command runs at most once.
  3. The hook receives JSON on stdin and hook-specific environment variables.

Hook Input (stdin)

A JSON payload is piped to the hook command:

{
  "event": "PreToolUse",
  "session_id": "abc-123",
  "cwd": "/path/to/project",
  "tool_name": "bash",
  "tool_input": {"command": "ls -la"}
}

Hook Environment Variables

Variable Description
CRUSH_EVENT Event name (e.g. PreToolUse)
CRUSH_TOOL_NAME Name of the tool being called
CRUSH_SESSION_ID Current session ID
CRUSH_CWD Current working directory
CRUSH_PROJECT_DIR Project root directory
CRUSH_TOOL_INPUT_COMMAND Value of command from tool input (if present)
CRUSH_TOOL_INPUT_FILE_PATH Value of file_path from tool input (if present)

Hook Output

Exit code 0 — the hook succeeded. Stdout is parsed as JSON:

{"decision": "allow", "context": "optional context appended to tool result"}
  • decision: allow to explicitly allow, deny to block, none (or omit) for no opinion.
  • reason: Explanation text (used when denying).
  • context: Extra context appended to the tool result.
  • updated_input: Replacement JSON for the tool input. Last non-empty value wins.

Exit code 2 — the tool call is blocked. Stderr is used as the deny reason.

echo "No Haskell allowed" >&2
exit 2

Any other exit code — non-blocking error. The tool call proceeds as normal.

Claude Code Compatibility

Crush also supports the Claude Code hook output format:

{
  "hookSpecificOutput": {
    "permissionDecision": "allow",
    "permissionDecisionReason": "Auto-approved",
    "updatedInput": {"command": "echo rewritten"}
  }
}

Existing Claude Code hooks should work without modification.

Decision Aggregation

When multiple hooks match, their decisions are aggregated:

  • Deny wins over allow — if any hook denies, the tool call is blocked.
  • Allow wins over none — if no hook denies but at least one allows, the call proceeds.
  • All deny reasons are concatenated (newline-separated).
  • All context strings are concatenated (newline-separated).
  • For updated_input, the last non-empty value wins.

Tool Permissions

{
  "permissions": {
    "allowed_tools": ["view", "ls", "grep", "edit"]
  }
}

Environment Variables

  • CRUSH_GLOBAL_CONFIG - Override global config location
  • CRUSH_GLOBAL_DATA - Override data directory location
  • CRUSH_SKILLS_DIR - Override default skills directory