Communitygithub.com

dzhng/crm.cli

管理联系人、公司、交易和管道,crm.cli 是一款无头优先、基于 CLI 的CRM,由 SQLite 驱动并支持虚拟文件系统接口。

crm.cli 是什么?

crm.cli 是一款专为终端和 AI 代理设计的无头、CLI 优先的客户关系管理工具。它将所有数据存储在一个 SQLite 文件中,支持通过终端直接查询,能与 Unix 工具组合使用,并且可以挂载为一个虚拟文件系统。该工具提供联系人、公司和交易的管理功能,包含完整的管道阶段管理、活动日志、FTS5 精确搜索和模糊语义搜索、基于 Levenshtein 和 Dice 系数的重复检测,以及丰富的报告生成能力。它支持通过 CLI 命令、CSV/JSON 导入导出、以及虚拟文件系统(FUSE/NFS)进行读写操作,非常适用于脚本自动化、AI 代理集成和批量管理场景。

兼容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/dzhng/crm.cli/tree/HEAD/skills

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

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;存储为干净的句柄

相关技能