Maintain meaningful relationships -- a CLI personal CRM backed by CardDAV.
Single binary, no database -- your contacts live in your existing address book, interaction history in a local JSONL file. Built to be driven by AI agents.
Read the docs | Getting Started | Command Reference | Agent Integration
Download a prebuilt binary from Releases, or install with Go:
go install github.com/justinabrahms/frm@latest
frm initto connect your CardDAV server (iCloud, Fastmail, or custom)frm triageto categorize your contactsfrm checkto see who you're overdue to reach out tofrm log "Alice" --note "caught up over coffee"after you talk to someone
frm is designed for AI agents to drive your relationship maintenance. Every command supports --json for structured output and --dry-run for safe previews.
# 1. Who needs attention?
frm check --json
# 2. Prep context before a meeting
frm context "Sarah Chen" --json
# 3. After the interaction, log it
frm log "Sarah Chen" --note "discussed Q2 roadmap"
# 4. Bulk-categorize new contacts
frm triage --json --limit -1
# then for each: frm track "<name>" --every 2w OR frm ignore "<name>"See the Agent Integration Guide and SKILL.md for a complete reference.
frm list List tracked contacts with due dates
frm list --all Include untracked contacts
frm check Show overdue contacts
frm context "Alice" Pre-meeting prep: summary + recent emails
frm log "Alice" --note "coffee" Log an interaction
frm log "Alice" --when 2026-02-15 Backdate an interaction
frm triage Walk through untagged contacts interactively
frm triage --json List untriaged contacts as JSON (for agents)
frm track "Alice" --every 2w Track Alice every 2 weeks
frm untrack "Alice" Stop tracking
frm ignore "Alice" Permanently hide from triage and check
frm unignore "Alice" Reverse an ignore
frm snooze "Alice" --until 2m Temporarily suppress from check
frm unsnooze "Alice" Remove a snooze
frm spread Preview staggered snoozes for new imports
frm spread --apply Apply the spread
frm add "Alice" --email a@b.com Create a new contact
frm edit "Alice" --phone "555" Update contact fields
frm history "Alice" Show interaction log
frm stats Dashboard
frm group set "Alice" friends Assign to a group
frm group unset "Alice" Remove from group
frm group list List all groups
frm group members friends List contacts in a group
All commands support --json for machine-readable output and --dry-run for previewing changes.
3d-- every 3 days2w-- every 2 weeks1m-- every month (30 days)
Create ~/.frm/config.json with at least one CardDAV service, or run frm init for the interactive wizard.
- Go to account.apple.com > Sign-In and Security > App-Specific Passwords
- Generate a new password
{
"services": [
{
"type": "carddav",
"endpoint": "https://contacts.icloud.com",
"username": "you@icloud.com",
"password": "xxxx-xxxx-xxxx-xxxx"
}
]
}{
"type": "carddav",
"endpoint": "https://carddav.fastmail.com/dav/addressbooks/user/you@example.com/Default",
"username": "you@example.com",
"password": "your-app-password"
}{
"type": "carddav",
"endpoint": "https://www.googleapis.com/.well-known/carddav",
"username": "you@gmail.com",
"password": "your-app-password"
}You can combine multiple CardDAV accounts and add a JMAP provider for email context in frm context and frm triage:
{
"services": [
{
"type": "carddav",
"endpoint": "https://contacts.icloud.com",
"username": "you@icloud.com",
"password": "xxxx-xxxx-xxxx-xxxx"
},
{
"type": "jmap",
"session_endpoint": "https://api.fastmail.com/jmap/session",
"token": "your-jmap-token",
"max_results": 3
}
]
}You can override the config directory with FRM_CONFIG_DIR.
Contacts and metadata live in your CardDAV server via custom vCard fields:
X-FRM-FREQUENCY-- tracking interval (e.g.2w,1m)X-FRM-IGNORE--"true"to permanently hideX-FRM-GROUP-- freeform group tagX-FRM-SNOOZE-UNTIL-- date to suppress until
Interaction history is stored locally in ~/.frm/log.jsonl. This is the only local state -- back it up or symlink it to a synced directory.
MIT
