Here's the thing — on Hacker News, the Charm team's "Glamourous AI coding agent for your favourite terminal" launch hit 367 points and 235 comments the day it shipped. Almost a year later, the repo has climbed to 25,233 GitHub stars and 1,805 forks, with the latest push on June 12, 2026. Yet when most engineers open Crush for the first time, they end up running it the way they run Claude Code — chat, edit, repeat. The README is long, but the most useful production features are hidden in the configuration file, the LSP wiring, the MCP section, and a tiny set of flags.
charmbracelet/crush is the agentic terminal tool from the Charm ecosystem (the same team behind bubbletea, lipgloss, and glow — the foundation powering roughly 25,000 downstream Go applications). It speaks OpenAI and Anthropic natively, ships three MCP transports, and runs the same binary on macOS, Linux, Windows (PowerShell + WSL), Android, FreeBSD, OpenBSD, and NetBSD. Most users see "yet another terminal AI." What's actually under the hood is closer to a programmable agent runtime with a real config language.
Let's walk through the five hidden uses that turned Crush from "my second coding agent" into the one I keep on top.
Hidden Use #1: Shell-Style Secret Expansion in crush.json
What most people do: Paste API keys directly into crush.json (and then regret it the first time the file lands in a git commit).
The hidden trick: Crush ships a bash-compatible expansion engine that runs over command, args, env, headers, and url fields in MCP, provider, and LSP config. That means you can use $VAR, ${VAR:-default}, $(command), and even ${VAR:?message} — the last one is the trick most engineers miss. Required-credential syntax fails loudly at load time instead of silently resolving to an empty string.
{
"$schema": "https://charm.land/crush.json",
"mcp": {
"github": {
"type": "http",
"url": "https://api.githubcopilot.com/mcp/",
"headers": {
"Authorization": "Bearer ${GH_PAT:?set GH_PAT in your shell env first}"
},
"timeout": 120
},
"filesystem": {
"type": "stdio",
"command": "node",
"args": ["/path/to/mcp-server.js"],
"env": {
"NODE_ENV": "production",
"TOKEN_FILE": "$(cat /run/secrets/mcp-token)"
}
}
}
}
The result: Headers whose value resolves to the empty string are dropped from the outgoing request entirely — Crush does not send Authorization: Bearer with a blank value. That means optional env-gated headers like OpenAI-Organization stay clean when unset, and required credentials fail fast with a meaningful error before the TUI ever renders.
Data sources: Crush 25,233 Stars / 1,805 Forks on GitHub (last push 2026-06-12), verified via the GitHub REST API. HN launch discussion 367 pts / 235 comments (objectID 44736176, 2025-07-30).
Hidden Use #2: Shared Workspaces Across Multiple TUI Clients
What most people do: Open one Crush window, finish, close it, lose the session.
The hidden trick: Run crush serve to expose an HTTP backend, then attach multiple TUI clients to the same workspace, keyed by --cwd. Two clients pointing at the same working directory join the same underlying workspace — they share the session list, message history, permission queue, LSP server pool, and MCP connections. The session picker exposes two signals: IsBusy (an agent turn is in flight) and AttachedClients (how many terminals are currently viewing the same session).
# Terminal A — start a long refactor and detach
crush serve --cwd ~/code/monolith --yolo
# Terminal B — watch the same workspace live
crush --cwd ~/code/monolith
# Terminal C on a laptop, SSH'd in — same workspace
crush --cwd ~/code/monolith
The result: A non-zero AttachedClients is the cue that a session is "in progress" on another client. The first client to create a workspace fixes process-wide flags (notably --yolo and --debug follow a first-wins rule — later clients cannot loosen security). The workspace lives as long as any client holds an SSE stream open against it; the moment the last stream disconnects, the workspace is torn down with a short grace window so a client that just created the workspace has time to attach.
Data sources: Crush README "Sharing a workspace across clients" section. Workspace lifecycle semantics verified against the README's documented grace-window and first-wins flag rules.
Hidden Use #3: Agent Skills with user-invocable and disable-model-invocation
What most people do: Skip skills because the docs read like a plugin marketplace.
The hidden trick: Crush supports the open Agent Skills standard (agentskills.io) and discovers them from seven default global paths plus three project-relative paths — and that's before you customize options.skills_paths. The killer feature is two specific frontmatter flags:
---
name: crush-config
description: "A skill that can be invoked as a command."
user-invocable: true
disable-model-invocation: true
---
# Walk the user through editing crush.json
Set user-invocable: true to surface the skill in the commands palette (Ctrl+P) as user:skill-name or project:skill-name. Set disable-model-invocation: true to hide the skill from the model entirely while still letting the human invoke it manually. This is how Crush ships its built-in crush-config skill — the model never auto-triggers it, but pressing Ctrl+P and typing "config" lets you ask the agent to walk you through editing its own config.
# Bootstrap from anthropics/skills as a starting point
mkdir -p ~/.config/crush/skills
cd ~/.config/crush/skills
git clone https://github.com/anthropics/skills.git _temp
mv _temp/skills/* . && rm -rf _temp
The result: You get a clean separation between human-invoked workflows (deploy, config, cleanup) and model-invoked skills (research, refactor, code search). The model can never accidentally fire a destructive skill, but the user can summon it on demand.
Data sources: Crush README "Agent Skills" section — seven global paths and three project-relative paths verified against the source. The agentskills.io open standard is referenced directly in the README.
Hidden Use #4: Custom Anthropic-Compatible Providers with Per-Model Cost Metadata
What most people do: Use the bundled preset providers and never touch the providers map.
The hidden trick: Crush supports two flavours of custom provider config — openai-compat for non-OpenAI APIs that use the OpenAI shape (Deepseek, Groq, Together, Fireworks), and anthropic for Anthropic-compatible endpoints (proxies, internal clusters, custom base URLs). The undocumented gem is the per-model metadata fields — cost_per_1m_in, cost_per_1m_out, cost_per_1m_in_cached, cost_per_1m_out_cached, context_window, default_max_tokens, can_reason, and supports_attachments. These aren't just decoration: the TUI uses them to render real cost estimates on every request.
{
"$schema": "https://charm.land/crush.json",
"providers": {
"deepseek": {
"type": "openai-compat",
"base_url": "https://api.deepseek.com/v1",
"api_key": "$DEEPSEEK_API_KEY",
"models": [
{
"id": "deepseek-chat",
"name": "Deepseek V3",
"cost_per_1m_in": 0.27,
"cost_per_1m_out": 1.1,
"cost_per_1m_in_cached": 0.07,
"context_window": 64000,
"default_max_tokens": 5000
}
]
},
"custom-anthropic": {
"type": "anthropic",
"base_url": "https://api.your-proxy.internal/v1",
"api_key": "$INTERNAL_ANTHROPIC_KEY",
"extra_headers": {
"anthropic-version": "2023-06-01",
"X-Tenant-Id": "$TENANT_ID"
},
"models": [
{
"id": "claude-sonnet-4-20250514",
"name": "Claude Sonnet 4",
"cost_per_1m_in": 3,
"cost_per_1m_out": 15,
"context_window": 200000,
"default_max_tokens": 50000,
"can_reason": true,
"supports_attachments": true
}
]
}
}
}
The result: Provider extra_body is a non-expanding JSON passthrough (use it for static fields), but extra_headers, api_key, and base_url all go through the shell-expansion engine. That means you can ship one crush.json to a team and let each engineer override credentials via environment — and the TUI will display real per-model dollar costs because the metadata is in the config, not hardcoded.
Data sources: Crush README "Custom Providers" section, Deepseek example config verified against the official README. Provider count (~25+ including OpenAI, Anthropic, Google, Groq, OpenRouter, Vercel, MiniMax, Z.ai, Cerebras, Hugging Face, AWS Bedrock, Azure OpenAI, etc.) verified via the env-var table in the README.
Hidden Use #5: Layered Tool and Skill Containment (.crushignore + disabled_tools + disabled_skills)
What most people do: Run with --yolo and hope for the best, or click through every permission prompt by hand.
The hidden trick: Crush gives you three independent containment layers that compose:
-
.crushignore— gitignore-syntax file that prevents files from entering Crush's context entirely (not just blocking edits — it never even reads them). -
options.disabled_tools— hides built-in tools (e.g.,"bash","sourcegraph") from the agent completely. The model cannot choose a tool it cannot see. -
options.disabled_skills— hides skills (built-in and discovered) from the agent. Pair this withdisable-model-invocationin skill frontmatter for the same effect on a per-skill basis. -
permissions.allowed_tools— the inverse whitelist. Only listed tools can be called without prompting.
{
"$schema": "https://charm.land/crush.json",
"options": {
"disabled_tools": ["bash"],
"disabled_skills": ["crush-config"],
"skills_paths": [
"~/.config/crush/skills",
"./project-skills"
]
},
"permissions": {
"allowed_tools": [
"view",
"ls",
"grep",
"edit",
"mcp_context7_get-library-doc"
]
}
}
# .crushignore — secrets, build artifacts, and large data
.env
.env.*
secrets/
*.pem
node_modules/
dist/
*.parquet
data/raw/
The result: You can deploy Crush to a team with bash disabled (forcing all shell work to go through vetted MCP tools), certain skills hidden (so the model never suggests crush-config mid-refactor), and an explicit allowlist of read-only tools. The .crushignore file uses the same syntax as .gitignore and stacks with it — so files you have already committed but never want the agent to consider are covered. This is the configuration pattern teams running Crush in customer-facing internal tools actually use.
Data sources: Crush README sections on "Ignoring Files", "Allowing Tools", "Disabling Built-In Tools", "Disabling Skills". All four mechanisms verified against the documented behavior in the README.
Recap: The Five Hidden Uses
-
Shell-style secret expansion with
${VAR:?msg}and$(command)for required-credential hygiene. -
Shared workspaces across multiple TUI clients via
crush serveand--cwdkeying. -
Agent Skills with
user-invocableanddisable-model-invocationfor human-vs-model skill routing. -
Custom Anthropic/OpenAI providers with per-model cost metadata and
extra_bodypassthrough. -
Layered containment via
.crushignore+disabled_tools+disabled_skills+allowed_tools.
Related Reading
- DeepClaude's 5 Hidden Uses: 17x Cheaper Claude Code Without Changing Your Workflow — if you love Crush's local-first ethos, this is the proxy that lets you keep using Claude Code at 1/17th the cost.
- KTransformers: 5 Hidden Uses of the 17K-Star MoE Inference Stack from Tsinghua — for when you want Crush's "multi-model, swap mid-session" idea extended to actually running 671B locally.
- Addy Osmani's Agent Skills: 5 Hidden Uses in 49K Stars of Workflow Magic — the open standard Crush's skill discovery implements, and how to use it in Claude Code and Cursor too.
What about you — what's the one config flag in your crush.json you wish you had known about earlier? Drop it in the comments; the Charm team is reading, and the next release picks the most-upvoted suggestions.
Top comments (0)