先建立坐标系:Charm 是谁,它为什么下场做 agent
charmbracelet(Charm)是终端圈最出名的 TUI 基础设施公司:Bubble Tea(Go 的 TUI 框架)、Lip Gloss(终端样式)、Glow(终端 Markdown)、Gum(shell 脚本里的 TUI 命令)全是它家的,README 自称「powering 25k+ applications」。crush 是它 2025 年 5 月下场做的 coding agent,口号是「Glamourous agentic coding for all 💘」——注意,这不是随便一个 vibe-coding 玩具:它是 Charm 认真押注的产品线,配套还养了一个自己的模型订阅服务 Hyper 和一个社区模型数据库 Catwalk。
跟工坊最近几篇的主角比,crush 的定位差异很明显:Gajae-Code 是「订阅 OAuth 外挂」,OpenClaude 是「Claude Code 改多后端」,MiMoCode 是「厂商闭环」——而 crush 是「老牌 TUI 厂把 agent 当成自家生态的旗舰应用来做」:模型自由(40+ provider)、配置即代码(crushrc)、LSP 一等公民、MCP 内置 OAuth。它不追求「某个模型调得最好」,追求「任何模型都能在终端里用得很顺」。血统纯正(Charm 出品)、迭代凶猛(nightly 日更、v0.90 → v0.91 三天一版)、但 669 个 open issues 也说明还在高速打磨期。
核心机制一:crushrc = 可执行 Bash——配置不是 JSON,是「带内置命令的 shell 脚本」
这是 crush 最反直觉也最 Charm 的设计:配置文件 crushrc 本质是 Bash,只是塞了一堆 Crush 内置命令(provider add / model add / mcp add / lsp add / permissions allow|deny / option)。因为 Crush 自带一个原生 Bash 解释器,同一份配置在 macOS / Linux / Windows 上行为完全一致。优先级从项目到全局:./.crushrc → ./crushrc → ~/.config/crush/crushrc(XDG)。
# crushrc 示例:加 Ollama、注册模型、放行工具、条件分支、加 MCP
provider add ollama --type ollama --base-url "http://localhost:11434/v1"
model add ollama/llama3.3 --name "Llama 3.3" --context-window 128000
permissions allow view edit # 免审批放行只读工具
if [[ $HOSTNAME == "babysquid" ]]; then
source ~/my-stuff/babysquid.sh # 真·shell,条件分支随便写
fi
mcp add github --type http --url "https://api.githubcopilot.com/mcp/" \
--header Authorization "Bearer $(op read 'op://my-secret-key')"
配置即代码的代价是安全面:README 自己用黑体警告——「crushrc 和 crush.json 都是 trusted code;crushrc 跑在完整 shell 里,crush.json 里任何 $(...) 都会在加载时执行」。也就是说,在没审过的目录里启动 crush,等于把 shell 执行权交给了那个目录的配置。别随便 clone 一个带 crushrc 的仓库就进去跑 agent。旧 JSON 格式(crush.json)仍支持但已标记 deprecated。
核心机制二:模型自由——40 家 provider、1,547 个模型、每天自动更新,外加自家订阅 Hyper
crush 的模型目录是「外置数据库」:启动时从 Catwalk(Charm 的社区模型仓库,789 Star)拉最新的 provider 和模型清单,自动写进本机配置。实测我第一次跑完,~/.local/share/crush/providers.json 里就有 40 家 provider、1,547 个模型——Anthropic / OpenAI / Gemini / DeepSeek / Groq / OpenRouter / Bedrock / Vertex / Kimi / GLM / MiniMax 全在,crush models 能直接列。想离线或固定版本:crush update-providers embedded 重置到内置清单,option provider-auto-update false 关自动更新。
两个值得记住的细节:
openai和openai-compat是两种 type,README 专门强调别选错——前者用于走 OpenAI 官方路由,后者用于一切 OpenAI 兼容第三方(DeepSeek、GLM、本地网关都算后者);选错会影响体验。- 本地模型自动发现:provider 用
ollama/llamacpp/lmstudio/litellm类型且不写死模型列表,crush 会自动拉 /models 目录填充。
Charm 还养了自己的模型订阅 Hyper:免费层 $0/月送 100 Hypercredits(1 credit = 5¢),订阅 $20/月拿 250 credits 每日刷新,主打零数据留存(ZDR)+ GDPR。有意思的是实测落盘的 hyper.json:Hyper 的默认大模型是 qwen3.7-plus、小模型是 deepseek-v4-flash-0731——Charm 自己不做模型,订阅背后路由的是开源/三方模型,跟它「coding-optimized open source models」的定位一致。
核心机制三:26 个工具的武器库,8 个 LSP 工具是一等公民——扒包实测
我用自定义 provider 指向本地 mock 服务跑通全链路后,把 crush 真实发出的请求扒了出来,信息量很大。先看工具面——默认 26 个工具:
| 类别 | 工具 |
|---|---|
| LSP(8 个) | lsp_definition · lsp_references · lsp_diagnostics · lsp_symbols · lsp_rename · lsp_replace_symbol · lsp_call_hierarchy · lsp_restart |
| 文件/代码 | view · edit · multiedit · write · glob · grep · ls |
| 执行 | bash · job_kill · job_output(后台任务) |
| 检索 | fetch · agentic_fetch · download · sourcegraph(公网代码搜索) |
| 编排 | agent(子代理)· todos(任务清单)· crush_info · crush_logs |
8 个 lsp_* 工具是 crush 最跟别人不一样的地方——「agent 像你一样用 LSP 看代码」,定义/引用/诊断/重命名全走语言服务器而不是正则硬猜。搭配 sourcegraph 内置公网代码搜索和 agent 子代理,这是个「IDE 级上下文 + 子代理并行」的配置。
请求体还暴露了几个值得知道的事实:
- system prompt 22,337 字符(约 5.6K tokens/请求):
<critical_rules>(14 条硬规则:先读后改、别问直接干、改完就跑测试、禁止 apply_patch 用 edit/multiedit、不主动 commit/push……)+ 20,750 字符的<available_skills>技能清单 +<skills_usage>强制加载流程。我一次crush run "hi"总共烧了 5,728 prompt tokens——大头就是这段系统提示。 - 两段式调用:主对话之外,每次还会发一个
max_tokens=40的「起标题」请求("You will generate a short title based on the first message")。也就是说每个会话至少 2 次 API 调用,小模型也不省。 - 客户端是 Stainless 生成的 Go SDK:请求头带
X-Stainless-Lang: go/X-Stainless-Package-Version: 3.50.0/X-Stainless-Retry-Count,UA 是Charm-Crush/v0.91.0;请求体带stream:true+stream_options+tool_choice。
核心机制四:MCP 内置 OAuth、Agent Skills 通吃各家目录、hooks 目前只有一个
MCP 支持 stdio / http / sse 三种传输,亮点是内置 OAuth 授权码流程:"oauth": true 就自动走 RFC 7591 动态客户端注册(Linear、Notion 这类直接可用),不支持动态注册的(GitHub、Slack)可以预注册 OAuth app 填 oauth_client_id/secret。实测项目里 mcp add 的 GitHub Copilot MCP 一行配置就带上了 Bearer $(op read ...) 的 shell 展开。
Agent Skills 走 agentskills.io 开放标准,发现目录覆盖得比谁都全:全局扫 ~/.config/agents/skills、~/.config/crush/skills、~/.agents/skills、~/.claude/skills(Windows 还有对应路径),项目里扫 .agents/skills、.crush/skills、.claude/skills、.cursor/skills——你给别的 agent 装的技能,crush 开机就能用。全局上下文文件也分两档:~/.config/crush/CRUSH.md(自家规则)+ ~/.config/AGENTS.md(跨工具通用规则)。
hooks 就寒酸了:目前只有 PreToolUse 一个事件(Claude Code 兼容格式,JSON 配置 + shell 脚本,可拦截/改写/注入/自动批准),README 明说「plans to support the full gamut」。想拿 hooks 做 git push 拦截之类的事,现在只能拦工具调用这一层。
上手:我在沙箱实测跑通的命令(含 mock 全链路验证)
Ubuntu 无头沙箱。全链路:下载 release 二进制 → 版本 → 命令面 → 无 key 裸跑 → 自定义 provider + 本地 mock(流式 SSE)→ 工具循环 → 坏 key → 会话管理,都走了一遍。
# 1) 安装(三选一;本文实测走 release 二进制) brew install charmbracelet/tap/crush # macOS npm install -g @charmland/crush # 跨平台 # 或下载 crush_0.91.0_Linux_x86_64.tar.gz(28.6MB → crush 单文件 94MB,sha256 校验通过) crush --version # crush version v0.91.0 # 2) 无 key 裸跑(报错友好,exit 1) crush run "say hi" # ERROR: No providers configured - please run 'crush' to set up a provider interactively. # 3) crushrc 配 DeepSeek(README 原样,openai-compat 类型) provider add deepseek --type openai-compat \ --base-url "https://api.deepseek.com/v1" --api-key "$DEEPSEEK_API_KEY" model add deepseek/deepseek-chat --name "Deepseek V3" \ --context-window 64000 --default-max-tokens 5000 \ --price-input 0.27 --price-output 1.1 # 4) 自定义 provider + 本地 mock 全链路(流式 SSE 正常) provider add mock --type openai-compat --base-url "http://127.0.0.1:9999/v1" --api-key "mock-key-123" model add mock/mock-chat --name "Mock Chat" --context-window 128000 --default-max-tokens 2000 crush run -m mock/mock-chat "用一句话介绍你自己" # → 你好,我是 mock。流式响应。exit 0 # 5) 工具循环实测:mock 让模型发 ls 工具调用,crush 真在本地执行 # 第二次请求里出现 roles=[system,user,user,assistant,tool],tool_result 是 /tmp/ws 真实目录树 # 6) 坏 key(mock 返回 401):报错清晰、exit 1,但失败后仍多发 2 次标题请求 crush run -m mockbad/mock-chat "hi" # ERROR: Agent processing failed: ... unauthorized: Incorrect API key provided # 7) 会话管理 crush session list --json # 每条含 id/title/created/cost/prompt_tokens/completion_tokens crush session show <id> --json # 消息带 finish reason:end_turn / error / stop crush run --continue "第二句" # 续最近一次会话(注意:不看成败) crush logs --follow # ./.crush/logs/crush.log crush stats # 生成 .crush/stats/index.html(headless 下打不开浏览器)
数据落盘结构也摸清了:全局在 ~/.local/share/crush/(providers.json 40 家 provider 全量、hyper.json、projects.json),每个项目一个 .crush/ 目录(SQLite crush.db + logs/crush.log + stats/index.html),会话、日志、统计全部按项目隔离。多客户端共享靠 crush server + 按 --cwd 归组 workspace,会话列表里 IsBusy / AttachedClients 两个信号能看出「这个会话另一个客户端正在看」。
对比:同赛道里的站位
| 维度 | Crush | Claude Code | OpenCode |
|---|---|---|---|
| 血统 | Charm(Bubble Tea 母公司) | Anthropic 官方 | anomalyco(社区) |
| 语言 / 体量 | Go 单二进制 94MB | 专有(npm/原生) | TypeScript / npm |
| 许可证 | ⚠️ FSL-1.1-MIT(source-available,两年后转 MIT,期间禁止竞争性商用) | ❌ 专有 | ✅ MIT |
| 模型接入 | ✅ 40 provider / 1,547 模型目录自动更新 + 本地自动发现 | ⚠️ 订阅或 API | ⚠️ 多后端需自配 |
| LSP | ✅ 8 个 lsp_* 工具一等公民 | ⚠️ 弱(靠 grep) | ✅ 支持 |
| MCP | ✅ stdio/http/sse + 内置 OAuth 授权码流程 | ✅ 支持 | ✅ 支持 |
| 配置 | crushrc(可执行 Bash + 内置命令) | CLAUDE.md 指令 | JSON / TS 配置 |
| 非交互 | ✅ crush run + stdin 管道 + --session | ✅ -p 打印模式 | ✅ run 命令 |
| 遥测 | ⚠️ PostHog 伪匿名,可关(CRUSH_DISABLE_METRICS / DO_NOT_TRACK) | ⚠️ 有 | ✅ 默认无 |
| Star(2026-08-26) | 27,677 | 142,962 | 201,287 |
一句话定位:Claude Code 是「官方模型 + 官方工具」,OpenCode 是「社区最开放的多后端」,crush 是「TUI 老厂把 agent 做成自家生态旗舰」——配置最 Geek(crushrc 可执行)、LSP 集成最深、模型目录最省心,但许可证最拧巴(FSL)。
实测与踩坑:6 个坑,前两个最要命
- 坑 1(最该知道):FSL-1.1-MIT 不是 OSI 开源,两年内不能做竞争性商用。README 徽章挂着 MIT 字样,但 LICENSE.md 正文是 Functional Source License:允许内部使用、教育、研究、给 licensee 提供服务,唯独禁止「用 crush 或功能实质相似的软件做商业产品或服务」替代 Charm 的生意;条款里写明第二周年自动转 MIT(Grant of Future License)。对你个人用没影响,但公司要做「基于 crush 的商业产品」就得算好这 2 年账,或者干脆选 MIT 的 OpenCode。
- 坑 2(实测 bug):非交互模式会话标题偶发丢失,日志报
sql: database is closed。每次crush run都会先发一个 max_tokens=40 的起标题请求,然后异步写库。实测:跑得快(几秒内结束)时写库线程撞上已关闭的数据库连接,标题保存失败、会话永远叫 Untitled Session;跑得慢(比如带工具循环)时能赶上,标题正常。竞态 bug,v0.91.0 实测复现,日志里ERROR Failed to save session title and usage: sql: database is closed。 - 坑 3:技能目录解析严格到刷屏。crush 开机扫描所有技能目录,SKILL.md 的 YAML frontmatter 解析失败就每条 WARN 一次。我这台机器上 ~/.agents/skills 里 28 个 lark 技能(别的工具留下的)全部「Failed to parse skill file: cannot unmarshal !!map into string」——跟 Claude Code 的宽容不同,crush 要求 frontmatter 字段是字符串,兼容性检查得自己过一遍。
- 坑 4:遥测 + 退出丢消息。crush 默认往 PostHog 发伪匿名用量(README 明说 prompts/responses 永不收集),沙箱无外网时日志里全是
Failed to flush PostHog events和shutdown timeout exceeded, some messages may be lost——退出时遥测 flush 卡住会拖慢关闭并丢日志消息。介意就 exportCRUSH_DISABLE_METRICS=1(或 DO_NOT_TRACK=1)。 - 坑 5:
--continue续的是「最近一次会话」,不看成败。我连续跑了「坏 key 失败会话」和「成功会话」,crush run --continue直接续到了失败那次——多客户端/多会话工作流里容易接错上下文。会话管理得靠session list先看 id 再--session <id>。 - 坑 6:每次请求的固定成本不低。system prompt 22,337 字符(约 5.6K tokens)+ 每个会话至少 2 次请求(主对话 + 起标题)。实测一句 "hi" 就烧 5,728 prompt tokens,长会话里技能清单还会跟着膨胀——用量敏感的人记得调
default-max-tokens和上下文窗口,别当它是零成本玩具。
值不值得装:三个场景对号入座
- 想要「一个终端、任意模型、开箱即配」的:值得装。40 家 provider / 1,547 个模型的目录自动更新是同类里最省心的,DeepSeek/GLM/Kimi 一行 crushrc 就接上,Ollama 本地模型自动发现;TUI 质感是 Charm 祖传手艺,比大多数 agent 的终端界面好看一个量级。
- 吃 LSP 红利、受够了 agent 用 grep 猜符号的:必试。8 个
lsp_*工具(定义/引用/诊断/重命名/调用层级)是 crush 最独特的能力,重构类任务它比纯文本工具的 agent 稳一截;前提是项目装了对应 language server。 - 想给团队统一 agent 配置的:crushrc 的 Bash 语法 + 全局/项目分级 +
crush server共享 workspace 是现成的方案;但先想清楚 FSL 许可证和「配置即 trusted code」的安全边界——审配置再进目录,遥测记得关。
最后说句公道话:27.7K Star 里没有 Star 通胀的味道(Charm 是十几年老厂,贡献者、release、文档都是真的),669 个 open issues 说明它还在高速打磨期——sql: database is closed 这种竞态就是例证。它跟工坊写过的 OpenClaude(Claude Code 改多后端)方向不同:OpenClaude 是「换引擎」,crush 是「自建整车 + 自家订阅 + 社区模型库」。FSL-1.1-MIT,仓库 github.com/charmbracelet/crush,安装 brew install charmbracelet/tap/crush 或 npm i -g @charmland/crush。「Glamourous agentic coding for all」——好看是真的,坑也是真的。