crm.cli

A headless, CLI-first CRM for developers who do sales. Contacts, deals, and pipeline in a single SQLite file — mountable as a virtual filesystem so AI agents get full CRM access for free.

$ npm install -g @dzhng/crm.cli

No server. No Docker. No accounts. No GUI. View on GitHub →

Built for how you actually work

📂

Your CRM is a filesystem

Mount with crm mount ~/crm and every tool that reads files — AI agents, shell scripts, editors — gets full CRM access. No MCP servers, no API keys. The filesystem is the universal API.

🤖

AI-native

Point Claude Code, Codex, or any AI agent at ~/crm and ask it to research your pipeline, draft outreach, or update deals. The agent reads and writes JSON files — zero integration code.

🔬

Deep normalization

E.164 phone normalization, website normalization, social handle extraction from URLs, entity merge with reference relinking, and fuzzy duplicate detection. What a spreadsheet can never provide.

🔧

Unix philosophy

Every command outputs structured data. Pipe to jq, grep, awk, or feed into scripts. --format json|csv|tsv|ids on everything. Composable by design.

💾

Single SQLite file

Everything lives in ~/.crm/crm.db. Back it up by copying the file. Share it via git, Dropbox, or rsync. No server, no network, no dependencies.

📊

Pipeline reports

Built-in reports: pipeline summary, stage conversion rates, deal velocity, weighted forecast, stale contacts, won/lost analysis. All from the command line, all pipeable.

Quick start

Install in one command, start managing contacts and deals in seconds.

Manage contacts

# Add a contact with full normalization
$ crm contact add --name "Jane Doe" \
    --email jane@acme.com \
    --phone "+1-212-555-1234" \
    --linkedin linkedin.com/in/janedoe \
    --company Acme
# Phone → E.164, LinkedIn URL → handle, company auto-linked

$ crm contact show jane@acme.com
$ crm contact show "(212) 555-1234"  # same contact — any format works

Track deals

$ crm deal add --title "Acme Enterprise" --value 50000 \
    --contact jane@acme.com --stage qualified

$ crm deal move dl_01J8Z... --stage negotiation
$ crm pipeline
# Visual pipeline with counts, values, weighted totals

$ crm report conversion
# Stage-to-stage conversion rates

Compose with Unix tools

# Bulk tag all contacts from Acme
$ crm contact list --company Acme --format ids | xargs -I{} crm tag {} enterprise

# Export pipeline as JSON, filter with jq
$ crm deal list --format json | jq '.[] | select(.value > 10000)'

# Import from CSV
$ crm import contacts leads.csv

Virtual filesystem for AI agents

Mount the CRM and every AI agent gets instant access. No MCP servers, no API keys, no integration code. Just files.

# Mount
$ crm mount ~/crm

# Agent reads instructions first
$ cat ~/crm/llm.txt

# Browse contacts
$ ls ~/crm/contacts/
$ cat ~/crm/contacts/ct_01...jane-doe.json

# Look up by email or phone
$ cat ~/crm/contacts/_by-email/jane@acme.com.json
$ cat ~/crm/contacts/_by-phone/+12125551234.json

# Pipeline stages
$ ls ~/crm/deals/_by-stage/qualified/

# Search via virtual files
$ cat ~/crm/search/"fintech CTO".json

# Reports
$ cat ~/crm/reports/forecast.json
# Filesystem layout
~/crm/
├── llm.txt              # agent instructions
├── contacts/
│   ├── ct_01...jane-doe.json
│   ├── _by-email/
│   ├── _by-phone/       # E.164 filenames
│   ├── _by-linkedin/
│   ├── _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-type/
├── reports/
│   ├── pipeline.json
│   ├── forecast.json
│   └── stale.json
├── search/              # filename = query
├── pipeline.json
└── tags.json