crm.cli 是做什么的?
一款无头、CLI 优先的CRM。联系人、交易和管道都存储在一个 SQLite 文件中——可以从终端查询、与 Unix 工具组合使用,并且可以挂载为虚拟文件系统。
安装
curl -fsSL https://raw.githubusercontent.com/dzhng/crm.cli/main/install.sh | sh
这会将预编译的二进制文件下载到 ~/.local/bin 并安装挂载依赖(Linux 上为 FUSE,macOS 上为 NFS 需要 Rust 工具链)。
安装后,确保 ~/.local/bin 在你的 PATH 中:
export PATH="$HOME/.local/bin:$PATH"
验证安装:
crm --version
配置
可选。在项目根目录或 ~/.crm/config.toml 中创建 crm.toml:
[database]
path = "~/.crm/crm.db"
[pipeline]
stages = ["lead", "qualified", "proposal", "negotiation", "closed-won", "closed-lost"]
won_stage = "closed-won"
lost_stage = "closed-lost"
[defaults]
format = "table"
[phone]
default_country = "US"
display = "international"
[mount]
default_path = "~/crm"
配置会自动从当前目录向上查找。可以通过 --config <path> 或 CRM_CONFIG 环境变量覆盖。
全局标志
每个命令都接受:
--db <path>— SQLite 数据库路径(默认:~/.crm/crm.db,环境变量:CRM_DB)--format <fmt>— 输出格式:table,json,csv,tsv,ids--config <path>— TOML 配置文件路径--no-color— 禁用彩色输出
联系人
创建联系人
crm contact add --name "Jane Doe" \
--email [email protected] \
--phone "+1-212-555-1234" \
--linkedin linkedin.com/in/janedoe \
--company "Acme Corp" \
--tag hot-lead \
--set title=CTO
除 --name 外,所有标志都是可选的。电话号码会被规范化为 E.164 格式,LinkedIn URL 会被提取为句柄,如果公司不存在则会自动创建。--email、--phone、--company、--tag 和 --set 都是可重复的。
社交句柄标志:--linkedin、--x、--bluesky、--telegram。接受原始句柄或完整 URL。
列出联系人
crm contact list
crm contact list --tag hot-lead --company "Acme Corp"
crm contact list --filter "title~=CTO AND company=Acme" --sort name --limit 20
crm contact list --format json | jq '.[].name'
过滤运算符:=、!=、~=(包含)、>、<。使用 AND / OR 组合。
显示联系人
通过 ID、电子邮件、电话或社交句柄查找:
crm contact show ct_01J8ZVXB3K...
crm contact show [email protected]
crm contact show "+12125551234"
crm contact show janedoe # LinkedIn 句柄
编辑联系人
crm contact edit [email protected] --name "Jane Smith"
crm contact edit "+12125551234" --add-email [email protected] --rm-tag old-tag
crm contact edit janedoe --add-company "New Corp" --set title=CEO --unset source
添加/删除标志:--add-email、--rm-email、--add-phone、--rm-phone、--add-company、--rm-company、--add-tag、--rm-tag。社交句柄直接设置:--linkedin、--x、--bluesky、--telegram。
删除联系人
crm contact rm [email protected]
crm contact rm "+12125551234" --force # 跳过确认
crm contact rm janedoe # 按社交句柄
合并联系人
将两个联系人合并为一个。第一个联系人保留,第二个被吸收并删除。接受任何引用类型:
crm contact merge ct_01A... ct_01B...
crm contact merge [email protected] [email protected]
crm contact merge "+12125551234" "+14155559876"
crm contact merge janedoe jane-doe-linkedin
合并邮箱、电话、公司、标签、自定义字段,并重新关联所有交易和活动。
公司
创建公司
crm company add --name "Acme Corp" \
--website acme.com \
--phone "+1-800-555-0000" \
--tag enterprise \
--set industry=SaaS
--website、--phone、--tag、--set 都是可重复的。
列出 / 显示 / 编辑 / 删除
crm company list --tag enterprise
crm company show acme.com # 按网站
crm company show "+18005550000" # 按电话
crm company edit acme.com --name "Acme Inc" --add-website acme.io
crm company rm acme.com --force
合并公司
crm company merge co_01A... co_01B...
crm company merge acme.com acme.io
crm company merge "+18005550000" "+18005550001"
将第二个公司的所有联系人和交易重新关联到第一个公司。
交易
创建交易
crm deal add --title "Acme Enterprise" \
--value 50000 \
--stage qualified \
--contact [email protected] \
--company acme.com \
--expected-close 2026-06-15 \
--probability 60 \
--tag enterprise
如果联系人和公司不存在,则会自动创建。--contact 和 --tag 是可重复的。
列出交易
crm deal list --stage qualified --min-value 10000
crm deal list --contact [email protected] --sort value --reverse
crm deal list --format ids | wc -l # 统计交易数量
在管道中移动交易
crm deal move dl_01... --stage proposal --note "已发送定价方案"
crm deal move dl_01... --stage closed-won --note "已签署2年合同"
阶段转换会作为活动记录并带有时间戳。请使用 deal move,而不是 deal edit --stage。
编辑 / 删除
crm deal edit dl_01... --value 75000 --add-contact [email protected] --probability 80
crm deal rm dl_01... --force
管道概览
crm pipeline
显示每个阶段的计数、总价值和加权价值。
活动记录
记录活动
crm log note "和Jane喝了咖啡,讨论了Q3扩张" --contact [email protected]
crm log call "演示了产品,她想要一份方案" --contact [email protected] --deal dl_01...
crm log meeting "季度复盘" --company acme.com --at 2026-04-01
crm log email "发送了后续定价" --contact [email protected] --set channel=outbound
类型:note、call、meeting、email。联系人和公司会自动创建。--contact 是可重复的。--at 覆盖时间戳。
列出活动
crm activity list --contact [email protected] --since 2026-01-01
crm activity list --type call --limit 10
crm activity list --deal dl_01... --format json
标签
crm tag [email protected] hot-lead enterprise # 添加标签
crm untag [email protected] old-tag # 删除标签
crm tag list # 所有标签及其计数
crm tag list --type contact # 仅联系人标签
标签适用于联系人、公司和交易。
搜索
精确关键词搜索(FTS5)
crm search "acme CTO"
crm search "jane" --type contact
模糊 / 语义搜索
crm find "fintech startup London"
crm find "我在会议上遇到的那个CTO" --limit 5 --threshold 0.3
重建搜索索引
crm index rebuild
crm index status
索引在写入时自动更新。仅在损坏后需要手动重建。
重复检测
crm dupes
crm dupes --type contact --threshold 0.5
crm dupes --type company --limit 20
使用 Levenshtein + Dice 系数组合相似度。检测:相似名称、共享邮箱、共享电话、共享网站、共享社交句柄。审查后合并:
crm dupes --type contact
# → Jane Doe ↔ J. Doe: 相似名称,共享邮箱
crm contact merge ct_01A... ct_01B...
报告
crm report pipeline # 阶段计数和金额
crm report activity --period 30d --by type # 活动量
crm report stale --days 14 --type contact # 近期无活动的
crm report conversion --since 2026-01-01 # 阶段间转化率
crm report velocity --won-only # 每个阶段停留时间
crm report forecast --period 2026-Q2 # 加权预测
crm report won --period 90d # 赢单总结
crm report lost --period 90d # 输单总结
导入 / 导出
从 CSV 或 JSON 导入
crm import contacts leads.csv
crm import contacts leads.json --update # 通过邮箱匹配更新现有
crm import companies companies.csv --dry-run
crm import deals deals.csv --skip-errors
cat data.json | crm import contacts - # 从标准输入导入
CSV 表头:name、email/emails、phone/phones、company/companies、tags、linkedin、x、bluesky、telegram。未识别的列将成为自定义字段。
导出
crm export contacts --format csv > contacts.csv
crm export companies --format json > companies.json
crm export deals --format tsv
crm export all --format json > full-backup.json
虚拟文件系统(挂载)
将 CRM 挂载为实时读写文件系统。任何可以读取文件的工具都能获得完整的 CRM 访问权限——AI 代理、grep、jq、vim、脚本。
挂载
crm mount ~/crm
crm mount ~/crm --readonly
在 Linux 上使用 FUSE,在 macOS 上使用 NFS v3 服务器(无需内核扩展)。
文件系统布局
~/crm/
├── llm.txt # 面向 AI 代理的说明
├── contacts/
│ ├── ct_01...jane-doe.json # 联系人 JSON 文件
│ ├── _by-email/ # 按邮箱查找
│ ├── _by-phone/ # 按 E.164 电话查找
│ ├── _by-linkedin/ # 按 LinkedIn 句柄查找
│ ├── _by-x/ # 按 X 句柄查找
│ ├── _by-company/ # 按公司分组
│ └── _by-tag/ # 按标签分组
├── companies/
│ ├── co_01...acme-corp.json
│ ├── _by-website/
│ ├── _by-phone/
│ └── _by-tag/
├── deals/
│ ├── dl_01...acme-enterprise.json
│ ├── _by-stage/
│ ├── _by-company/
│ └── _by-tag/
├── activities/
│ ├── _by-contact/
│ ├── _by-company/
│ ├── _by-deal/
│ └── _by-type/
├── reports/ # 预计算的分析
│ ├── pipeline.json
│ ├── forecast.json
│ ├── stale.json
│ ├── conversion.json
│ ├── velocity.json
│ ├── won.json
│ └── lost.json
├── pipeline.json # 快速管道概览
├── tags.json # 所有标签及其计数
└── search/ # 通过读取文件进行搜索
└── <query>.json # cat search/"acme CTO".json
通过文件系统读取
ls ~/crm/contacts/
cat ~/crm/contacts/ct_01...jane-doe.json | jq .
cat ~/crm/contacts/_by-email/[email protected]
cat ~/crm/deals/_by-stage/qualified/
cat ~/crm/reports/forecast.json
cat ~/crm/search/"enterprise deals".json
通过文件系统写入
# 创建联系人
echo '{"name":"Bob Smith","emails":["[email protected]"]}' > ~/crm/contacts/new.json
# 更新(读取 → 修改 → 写回)
cat ~/crm/contacts/ct_01...jane-doe.json | jq '.tags += ["vip"]' > ~/crm/contacts/ct_01...jane-doe.json
# 删除
rm ~/crm/contacts/ct_01...jane-doe.json
卸载
crm unmount ~/crm
静态导出(无需挂载)
crm export-fs ./crm-snapshot
导出相同的目录结构作为静态副本——适用于容器或沙箱环境,其中 FUSE 不可用。
批量操作
使用 --format ids 将输出传递给其他命令:
# 将所有 Acme 公司的联系人标记为企业
crm contact list --company "Acme Corp" --format ids | xargs -I{} crm tag {} enterprise
# 将所有金额超过 5 万美元的合格交易移动到方案阶段
crm deal list --stage qualified --min-value 50000 --format ids | \
xargs -I{} crm deal move {} --stage proposal
# 删除所有长时间未活动的联系人
crm report stale --days 90 --type contact --format ids | xargs -I{} crm contact rm {} --force
自定义字段
所有实体都支持任意键值字段:
crm contact add --name "Jane" --set title=CTO --set source=conference
crm contact edit [email protected] --set "json:score=85" --set "json:verified=true"
crm contact edit [email protected] --unset source
crm contact list --filter "title~=CTO"
使用 json: 前缀设置类型化值(数字、布尔值、数组)。
钩子
在 crm.toml 中配置 shell 钩子,在数据变更时触发:
[hooks]
post-contact-add = "~/.crm/hooks/notify-slack.sh"
post-deal-stage-change = "~/.crm/hooks/deal-moved.sh"
pre-contact-rm = "~/.crm/hooks/confirm-delete.sh"
实体数据通过标准输入以 JSON 格式传递。预钩子(pre-hooks)在非零退出时中止操作。
可用的钩子:{pre,post}-{contact,company,deal}-{add,edit,rm}、{pre,post}-deal-stage-change、{pre,post}-activity-add。
给 AI 代理的提示
- 首先挂载:
crm mount ~/crm赋予你文件系统访问权限——直接读取 JSON 文件,无需运行 CLI 命令 - 阅读
llm.txt: 挂载点包含llm.txt,里面有结构文档和提示 - 使用
_by-*目录 进行快速查找:_by-email、_by-phone、_by-linkedin、_by-tag、_by-stage - 在程序化处理时使用
--format json输出所有 CLI 结果 - 使用
--format ids+xargs进行批量操作 - 阅读
reports/获取预计算的分析结果——不要从原始数据重新计算 - 通过文件系统搜索:
cat ~/crm/search/"你的查询".json - 通过文件系统写入: 通过写入 JSON 文件来创建/更新实体
- 所有 JSON 文件都是独立的 —— 无需跨文件关联
- 电话号码 输入接受任何格式;内部存储为 E.164
- 社交句柄 接受完整 URL;存储为干净的句柄