TL;DR: HubSpot takes days to configure. A folder of Markdown files + one Python script + one AI agent = a CRM you customize in minutes, own entirely, and that AI reads natively. Zero subscriptions. Just your data.
The SaaS trap
CRM tools (Salesforce, HubSpot, Pipedrive, etc.) share a dirty secret: they’re built for management reporting, not for sales. Required fields everywhere. Pipeline stages locked behind an admin panel. Custom fields hidden in a form builder. Data exports behind a support ticket.
You spend days configuring the tool, then you spend more time filling it in than selling. That’s the bait and switch.
The plain-text way
A CRM is just a structured list of people, their pipeline stage, and your history with them. That fits in a folder.
- One Markdown file per contact
- Frontmatter — a small info block at the top of each markdown file, between
---dashes, that stores fields like stage, last contact, and company so the AI and your scripts can filter by them - Each interaction (call, email, meeting) gets its own file, with a summary line appended to the contact’s main page — history grows without cluttering
- Pipeline visualized by the Obsidian Kanban plugin
- Dashboard generated by a script, in Python so it works on any machine
- An AI agent that creates contacts, logs interactions, and updates stages so you never touch a form
The AI agent is the key. Without it, you’re still filling files by hand. With it, you talk to your CRM: “Log this call with Jane, she’s ready for a demo.” Done.
Step 1: Create the folder structure
CRM/
├── _templates/
│ ├── Person.md
│ ├── Company.md
│ └── Interaction.md
├── Persons/
│ └── {First_Last}/
│ ├── {First_Last}.md
│ └── {YYYY-MM-DD}_{TYPE}.md
├── Companies/
├── Dashboard.md
└── Pipeline.md
That’s it. No database. No SaaS. A folder.
Step 2: Write your Person template
This is the schema. It defines every field your contacts need. Don’t write it from scratch — grab the templates from the migration-toolkit:
Person.md— the contact template shown belowCompany.md— the thin account pageInteraction.md— the append-only event log
Copy them into your CRM/_templates/ folder and customize to your needs.
CRM/_templates/Person.md:
---
aliases:
- "[FIRST_NAME]"
- "[LAST_NAME]"
- "[EMAIL]"
tags:
- CRM/Contact
- CRM/Company_[COMPANY_NAME]
created: "[DATE]"
last_contact: "[DATE]"
stage: Lead
health_score: Unknown
preferred_channel: "[PREFERRED_CHANNEL]"
type: None
---
# [FIRST_NAME] [LAST_NAME]
**Email:** [EMAIL]
**Title:** [TITLE]
**Phone:** [PHONE]
**LinkedIn:** [LINKEDIN_URL]
**Preferred Contact:** [PREFERRED_CHANNEL]
## Executive Summary
## Interaction Log
## Notes & Action Items
- [ ]
Five fields define your pipeline:
- stage — Lead, Demo, Trial, Negotiation, Closed Won, Closed Lost, Churned
- last_contact — drives follow-up urgency
- health_score — Unknown, At Risk, Stable, Growing, Champion
- tags — company association (one person, multiple companies)
- type — None, Advisor, Investor, Competitor, Partner, Influencer
Want different stages? Change the YAML. No admin panel required.
The Company template works the same way — a thin anchor page with website, industry, size, and status fields. Grab Company.md from the toolkit.
Step 3: Set up the Kanban pipeline
The Obsidian Kanban plugin turns any Markdown file with --- section dividers into a visual board. It’s not installed by default.
- Open Obsidian → Settings → Community Plugins
- Browse for “Kanban” → Install → Enable
- Create
Pipeline.mdwith your stages as headers:
---
kanban-plugin: board
---
# CRM Pipeline
## Lead
## Demo
## Trial
## Negotiation
## Closed Won
## Closed Lost
## Churned
It shows in Obsidian like:
The plugin reads each ## Header as a column. Drag contacts between columns to change their stage. The underlying file updates automatically — no SQL, no API.
Step 4: Build the dashboard generator
A short Python script scans every person file, reads the frontmatter, and builds a view of what needs attention: pending action items, contacts needing follow-up, stale leads.
You don’t need to write it yourself. The full scripts — generate_dashboard.py and generate_pipeline.py — are in the migration-toolkit.
To run it:
python scripts/generate_dashboard.py
Or just ask your AI agent to run it for you. That’s the whole point.
The script finds every unchecked task across all contacts and builds a single todo list. Extend it to show stale contacts, contacts by stage, or stakeholders by type.
The full version used by AmpereBrain (100+ contacts, live CRM) does exactly this plus generates a pipeline view with most recently updated contact on top.
Step 5: Add the AI agent (this is the point)
Without an AI agent, you’re still filling files manually. The agent makes the CRM feel like a product — you talk to it, it does the work.
Create an AI agent that can create contacts and log interactions. The agent encodes the rules humans forget: deduplication, file naming, default field values, tag normalization, dashboard regeneration.
You can run this as an agent (a dedicated mode) or as a skill (always loaded, triggers on natural language). Both ship ready-to-use in the migration-toolkit — pick whichever fits your workflow:
- As a skill:
crm-creator/SKILL.mdandcrm-quick-update/SKILL.md→ drop into.skill/at the root of your vault. - As an agent:
agents/crm-creator.mdandagents/crm-quick-update.md→ drop into.opencode/agent/
Adapt the rules to your exact needs.
crm-creator.md:
---
description: CRM Creator — creates contacts, detects duplicates, sets up company links.
mode: primary
---
You create and update CRM records from natural language input.
## File Conventions
- Persons: `CRM/Persons/{First_Last}/{First_Last}.md`
- Companies: `CRM/Companies/{Company_Name}.md`
- Company association: use **tags** (`CRM/Company_{Name}`), NOT wikilinks
## Workflow: Create Person
1. Read template from `CRM/_templates/Person.md`
2. Check for duplicates by email — if exists, return existing and stop
3. Create folder `CRM/Persons/{First_Last}/`, copy template
4. Populate: name, email, title, phone, created/last_contact (today), stage (Lead)
5. Add company tag: `CRM/Company_{Name}` (normalized: remove Inc, LLC, Corp)
6. If company doesn't exist, create it from `CRM/_templates/Company.md`
7. Run: `python scripts/generate_dashboard.py`
crm-quick-update.md:
---
description: CRM Quick Update — logs calls, emails, transcripts, updates stages.
mode: primary
---
You update existing CRM records from conversation or input.
## File Conventions
- Persons: `CRM/Persons/{First_Last}/{First_Last}.md`
- Interaction template: `CRM/_templates/Interaction.md`
- Interaction file: `CRM/Persons/{First_Last}/{YYYY-MM-DD}_{TYPE}.md`
## Workflow
1. Identify person by name or email — if not found, suggest creating
2. Read interaction template, create interaction file with summary + action items
3. Update person file: append to Interaction Log, update `last_contact`, update `stage` if signaled
4. Run: `python scripts/generate_dashboard.py`
Now you say: “Add John Doe from Acme Corp, [email protected], VP Engineering” — and the agent creates the folder, fills the template, checks for duplicates, creates the company page, adds the tag, regenerates the dashboard. In seconds.
What you gain
| HubSpot / Salesforce | This |
|---|---|
| Days to configure pipeline | Minutes — change a YAML field |
| Custom fields = form builder | Edit a template file |
| New pipeline stage? Admin UI | Add a line to the template |
| Data behind “Request Export” | You own the folder |
| Multiple companies? New deal | Add a tag |
| AI integration requires API | AI reads it natively |
| $50–$150+/seat/month | Cents of LLM usage |
| Filling = clicking forms | Filling = talk to your agent |
| Backup = enterprise plan | It’s a folder. Zip it. Git it. Done. |
You’ll save more time than you spend setting it up. The CRM fills itself.
Going further: automations
Once your CRM is plain text, the automations write themselves.
Inbound
- Contact form → CRM → In your email drafts. Someone fills the form on your website? Their name, email, and company land in the CRM automatically. Your AI agent searches LinkedIn, scans the company website, and prefills the Executive Summary and account notes. In your 9am morning briefing, you already have a draft email written to reply to them.
Outbound
- Voice AI for discovery. Running customer discovery calls? A voice AI can conduct the interview, transcribe it, and log the result straight into the CRM — interaction file, summary, action items, stage update. All before you’ve finished your coffee.
Deals
- Proposal drafting. “Draft a proposal for Jane based on our last three calls.” The agent reads the interaction files and writes something that actually matches her tone and concerns.
The CRM stops being a database you maintain and becomes a system that maintains itself.
Real-world example: AmpereBrain
AmpereBrain (an automation tool for hardware engineers) runs its outreach on exactly this system. 100+ contacts in the pipeline, dashboard with 127 tracked action items, kanban board sorted by recency. Two OpenCode agents handle contact creation and interaction logging. The Python generators rebuild the views after every change.
The CRM is a folder. That folder is backed by Git, queryable by AI, and costs nothing to run.
What’s next
Your company is AI-native. Keep evolving:
- Revisit the Manifesto — the principles behind the stack.
- See the AmpereBrain Case Study for real-world automation patterns.
- Need help migrating or setting up your system? DM Charles on LinkedIn.
Your data. Your rules. Let’s write it that way. By Charles Henri Gayot.
