internal/skills/builtin/crush-config/SKILL.md
Crush uses JSON configuration files with the following priority (highest to lowest):
.crush.json (project-local, hidden)crush.json (project-local)$XDG_CONFIG_HOME/crush/crush.json or $HOME/.config/crush/crush.json (global){
"$schema": "https://charm.land/crush.json",
"models": {},
"providers": {},
"mcp": {},
"lsp": {},
"hooks": {},
"options": {},
"permissions": {},
"tools": {}
}
The $schema property enables IDE autocomplete but is optional.
providers with type, base_url, api_key, and models.options.disabled_skills.mcp with type and either command (stdio) or url (http/sse).{
"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.model and provider are required.reasoning_effort, think, max_tokens, temperature, top_p, top_k, frequency_penalty, presence_penalty, provider_options.{
"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 anthropicapi_key supports $ENV_VAR syntax.disable, system_prompt_prefix, extra_headers, extra_body, provider_options.{
"lsp": {
"go": {
"command": "gopls",
"env": { "GOTOOLCHAIN": "go1.24.5" }
},
"typescript": {
"command": "typescript-language-server",
"args": ["--stdio"]
}
}
}
command (required), args, env cover most setups.disabled, filetypes, root_markers, init_options, options, timeout.{
"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 httpenv, disabled, disabled_tools, timeout.{
"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 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"
}
]
}
}
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 names are case-insensitive and accept snake_case variants: PreToolUse, pretooluse, pre_tool_use, and PRE_TOOL_USE all work.
PreToolUse hooks with a matching matcher (or no matcher) run in parallel.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"}
}
| 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) |
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.
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.
When multiple hooks match, their decisions are aggregated:
updated_input, the last non-empty value wins.{
"permissions": {
"allowed_tools": ["view", "ls", "grep", "edit"]
}
}
CRUSH_GLOBAL_CONFIG - Override global config locationCRUSH_GLOBAL_DATA - Override data directory locationCRUSH_SKILLS_DIR - Override default skills directory