SKILL.md

  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