1---
2name: crush-config
3description: 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.
4---
5
6# Crush Configuration
7
8Crush uses JSON configuration files with the following priority (highest to lowest):
9
101. `.crush.json` (project-local, hidden)
112. `crush.json` (project-local)
123. `$XDG_CONFIG_HOME/crush/crush.json` or `$HOME/.config/crush/crush.json` (global)
13
14## Basic Structure
15
16```json
17{
18 "$schema": "https://charm.land/crush.json",
19 "models": {},
20 "providers": {},
21 "mcp": {},
22 "lsp": {},
23 "hooks": {},
24 "options": {},
25 "permissions": {},
26 "tools": {}
27}
28```
29
30The `$schema` property enables IDE autocomplete but is optional.
31
32## Common Tasks
33
34- Add a custom provider: add an entry under `providers` with `type`, `base_url`, `api_key`, and `models`.
35- Disable a builtin or local skill: add the skill name to `options.disabled_skills`.
36- Add an MCP server: add an entry under `mcp` with `type` and either `command` (stdio) or `url` (http/sse).
37
38## Model Selection
39
40```json
41{
42 "models": {
43 "large": {
44 "model": "claude-sonnet-4-20250514",
45 "provider": "anthropic",
46 "max_tokens": 16384
47 },
48 "small": {
49 "model": "claude-haiku-4-20250514",
50 "provider": "anthropic"
51 }
52 }
53}
54```
55
56- `large` is the primary coding model; `small` is for summarization.
57- Only `model` and `provider` are required.
58- Optional tuning: `reasoning_effort`, `think`, `max_tokens`, `temperature`, `top_p`, `top_k`, `frequency_penalty`, `presence_penalty`, `provider_options`.
59
60## Custom Providers
61
62```json
63{
64 "providers": {
65 "deepseek": {
66 "type": "openai-compat",
67 "base_url": "https://api.deepseek.com/v1",
68 "api_key": "$DEEPSEEK_API_KEY",
69 "models": [
70 {
71 "id": "deepseek-chat",
72 "name": "Deepseek V3",
73 "context_window": 64000
74 }
75 ]
76 }
77 }
78}
79```
80
81- `type` (required): `openai`, `openai-compat`, or `anthropic`
82- `api_key` supports `$ENV_VAR` syntax.
83- Additional fields: `disable`, `system_prompt_prefix`, `extra_headers`, `extra_body`, `provider_options`.
84
85## LSP Configuration
86
87```json
88{
89 "lsp": {
90 "go": {
91 "command": "gopls",
92 "env": { "GOTOOLCHAIN": "go1.24.5" }
93 },
94 "typescript": {
95 "command": "typescript-language-server",
96 "args": ["--stdio"]
97 }
98 }
99}
100```
101
102- `command` (required), `args`, `env` cover most setups.
103- Additional fields: `disabled`, `filetypes`, `root_markers`, `init_options`, `options`, `timeout`.
104
105## MCP Servers
106
107```json
108{
109 "mcp": {
110 "filesystem": {
111 "type": "stdio",
112 "command": "node",
113 "args": ["/path/to/mcp-server.js"]
114 },
115 "github": {
116 "type": "http",
117 "url": "https://api.githubcopilot.com/mcp/",
118 "headers": {
119 "Authorization": "Bearer $GH_PAT"
120 }
121 }
122 }
123}
124```
125
126- `type` (required): `stdio`, `sse`, or `http`
127- Additional fields: `env`, `disabled`, `disabled_tools`, `timeout`.
128
129## Options
130
131```json
132{
133 "options": {
134 "skills_paths": ["./skills"],
135 "disabled_tools": ["bash", "sourcegraph"],
136 "disabled_skills": ["crush-config"],
137 "tui": {
138 "compact_mode": false,
139 "diff_mode": "unified",
140 "transparent": false
141 },
142 "auto_lsp": true,
143 "debug": false,
144 "debug_lsp": false,
145 "attribution": {
146 "trailer_style": "assisted-by",
147 "generated_with": true
148 }
149 }
150}
151```
152
153> [!IMPORTANT]
154> The following skill paths are loaded by default and DO NOT NEED to be added to `skills_paths`:
155> `.agents/skills`, `.crush/skills`, `.claude/skills`, `.cursor/skills`
156
157Other options: `context_paths`, `progress`, `disable_notifications`, `disable_auto_summarize`, `disable_metrics`, `disable_provider_auto_update`, `disable_default_providers`, `data_directory`, `initialize_as`.
158
159## Hooks
160
161Hooks are user-defined shell commands that fire on agent events. Currently only `PreToolUse` is supported, which runs before a tool is executed.
162
163```json
164{
165 "hooks": {
166 "PreToolUse": [
167 {
168 "matcher": "^(edit|write|multiedit)$",
169 "command": ".crush/hooks/protect-files.sh"
170 },
171 {
172 "matcher": "^bash$",
173 "command": ".crush/hooks/no-haskell.sh"
174 }
175 ]
176 }
177}
178```
179
180### Hook Properties
181
182- `command` (required): Shell command to execute. Runs via `sh -c`.
183- `matcher` (optional): Regex pattern tested against the tool name. Empty or absent means match all tools.
184- `timeout` (optional): Timeout in seconds. Defaults to 30.
185
186### Event Name Normalization
187
188Event names are case-insensitive and accept snake_case variants: `PreToolUse`, `pretooluse`, `pre_tool_use`, and `PRE_TOOL_USE` all work.
189
190### How Hooks Work
191
1921. When a tool is about to be called, all `PreToolUse` hooks with a matching `matcher` (or no matcher) run in parallel.
1932. Duplicate commands are deduplicated — each unique command runs at most once.
1943. The hook receives JSON on **stdin** and hook-specific **environment variables**.
195
196### Hook Input (stdin)
197
198A JSON payload is piped to the hook command:
199
200```json
201{
202 "event": "PreToolUse",
203 "session_id": "abc-123",
204 "cwd": "/path/to/project",
205 "tool_name": "bash",
206 "tool_input": {"command": "ls -la"}
207}
208```
209
210### Hook Environment Variables
211
212| Variable | Description |
213|---|---|
214| `CRUSH_EVENT` | Event name (e.g. `PreToolUse`) |
215| `CRUSH_TOOL_NAME` | Name of the tool being called |
216| `CRUSH_SESSION_ID` | Current session ID |
217| `CRUSH_CWD` | Current working directory |
218| `CRUSH_PROJECT_DIR` | Project root directory |
219| `CRUSH_TOOL_INPUT_COMMAND` | Value of `command` from tool input (if present) |
220| `CRUSH_TOOL_INPUT_FILE_PATH` | Value of `file_path` from tool input (if present) |
221
222### Hook Output
223
224**Exit code 0** — the hook succeeded. Stdout is parsed as JSON:
225
226```json
227{"decision": "allow", "context": "optional context appended to tool result"}
228```
229
230- `decision`: `allow` to explicitly allow, `deny` to block, `none` (or omit) for no opinion.
231- `reason`: Explanation text (used when denying).
232- `context`: Extra context appended to the tool result.
233- `updated_input`: Replacement JSON for the tool input. Last non-empty value wins.
234
235**Exit code 2** — the tool call is blocked. Stderr is used as the deny reason.
236
237```bash
238echo "No Haskell allowed" >&2
239exit 2
240```
241
242**Any other exit code** — non-blocking error. The tool call proceeds as normal.
243
244### Claude Code Compatibility
245
246Crush also supports the Claude Code hook output format:
247
248```json
249{
250 "hookSpecificOutput": {
251 "permissionDecision": "allow",
252 "permissionDecisionReason": "Auto-approved",
253 "updatedInput": {"command": "echo rewritten"}
254 }
255}
256```
257
258Existing Claude Code hooks should work without modification.
259
260### Decision Aggregation
261
262When multiple hooks match, their decisions are aggregated:
263
264- **Deny wins over allow** — if any hook denies, the tool call is blocked.
265- **Allow wins over none** — if no hook denies but at least one allows, the call proceeds.
266- All deny reasons are concatenated (newline-separated).
267- All context strings are concatenated (newline-separated).
268- For `updated_input`, the last non-empty value wins.
269
270## Tool Permissions
271
272```json
273{
274 "permissions": {
275 "allowed_tools": ["view", "ls", "grep", "edit"]
276 }
277}
278```
279
280## Environment Variables
281
282- `CRUSH_GLOBAL_CONFIG` - Override global config location
283- `CRUSH_GLOBAL_DATA` - Override data directory location
284- `CRUSH_SKILLS_DIR` - Override default skills directory