152 lines
4.0 KiB
Python
Executable File
152 lines
4.0 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
people - Personal CRM / people notes
|
|
|
|
Keep notes about people you know.
|
|
"""
|
|
|
|
import json
|
|
import sys
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
|
|
WORKSPACE = Path("/home/wdjones/.openclaw/workspace")
|
|
PEOPLE_FILE = WORKSPACE / "data" / "people.json"
|
|
|
|
def load_people() -> dict:
|
|
"""Load people data."""
|
|
PEOPLE_FILE.parent.mkdir(parents=True, exist_ok=True)
|
|
if PEOPLE_FILE.exists():
|
|
with open(PEOPLE_FILE) as f:
|
|
return json.load(f)
|
|
return {}
|
|
|
|
def save_people(data: dict):
|
|
"""Save people data."""
|
|
with open(PEOPLE_FILE, 'w') as f:
|
|
json.dump(data, f, indent=2)
|
|
|
|
def add_person(name: str, context: str = None):
|
|
"""Add a new person."""
|
|
people = load_people()
|
|
key = name.lower().replace(' ', '_')
|
|
|
|
if key in people:
|
|
print(f"Person already exists: {name}")
|
|
return
|
|
|
|
people[key] = {
|
|
'name': name,
|
|
'context': context,
|
|
'added': datetime.now().isoformat(),
|
|
'notes': [],
|
|
'tags': [],
|
|
'last_contact': None,
|
|
}
|
|
|
|
save_people(people)
|
|
print(f"👤 Added: {name}")
|
|
|
|
def add_note(name: str, note: str):
|
|
"""Add a note about someone."""
|
|
people = load_people()
|
|
search = name.lower().replace(' ', '')
|
|
|
|
# Find by partial match (normalize spaces)
|
|
matches = [k for k in people if search in k.replace('_', '') or search in people[k]['name'].lower().replace(' ', '')]
|
|
if not matches:
|
|
print(f"Person not found: {name}")
|
|
return
|
|
|
|
key = matches[0]
|
|
people[key]['notes'].append({
|
|
'text': note,
|
|
'date': datetime.now().isoformat(),
|
|
})
|
|
people[key]['last_contact'] = datetime.now().isoformat()
|
|
|
|
save_people(people)
|
|
print(f"📝 Note added for {people[key]['name']}")
|
|
|
|
def show_person(name: str):
|
|
"""Show info about a person."""
|
|
people = load_people()
|
|
search = name.lower().replace(' ', '')
|
|
|
|
# Find by partial match (normalize spaces)
|
|
matches = [k for k in people if search in k.replace('_', '') or search in people[k]['name'].lower().replace(' ', '')]
|
|
if not matches:
|
|
print(f"Person not found: {name}")
|
|
return
|
|
|
|
person = people[matches[0]]
|
|
|
|
print(f"\n👤 {person['name']}")
|
|
print("=" * 40)
|
|
|
|
if person.get('context'):
|
|
print(f"Context: {person['context']}")
|
|
|
|
if person.get('last_contact'):
|
|
last = person['last_contact'][:10]
|
|
print(f"Last contact: {last}")
|
|
|
|
if person['notes']:
|
|
print(f"\n📝 Notes ({len(person['notes'])})")
|
|
for note in person['notes'][-5:]:
|
|
date = note['date'][:10]
|
|
print(f" [{date}] {note['text'][:50]}")
|
|
|
|
print()
|
|
|
|
def list_people():
|
|
"""List all people."""
|
|
people = load_people()
|
|
|
|
if not people:
|
|
print("No people yet. Add with: people add <name>")
|
|
return
|
|
|
|
print(f"\n👥 People ({len(people)})")
|
|
print("=" * 40)
|
|
|
|
for key, person in sorted(people.items()):
|
|
notes = len(person['notes'])
|
|
context = person.get('context', '')[:20]
|
|
print(f" {person['name']:20} {notes} notes {context}")
|
|
|
|
print()
|
|
|
|
def main():
|
|
if len(sys.argv) < 2:
|
|
list_people()
|
|
return
|
|
|
|
cmd = sys.argv[1]
|
|
|
|
if cmd == 'add' and len(sys.argv) > 2:
|
|
name = sys.argv[2]
|
|
context = ' '.join(sys.argv[3:]) if len(sys.argv) > 3 else None
|
|
add_person(name, context)
|
|
|
|
elif cmd == 'note' and len(sys.argv) > 3:
|
|
name = sys.argv[2]
|
|
note = ' '.join(sys.argv[3:])
|
|
add_note(name, note)
|
|
|
|
elif cmd == 'show' and len(sys.argv) > 2:
|
|
show_person(sys.argv[2])
|
|
|
|
elif cmd == 'list':
|
|
list_people()
|
|
|
|
else:
|
|
print("Usage:")
|
|
print(" people - List all")
|
|
print(" people add <name> [context] - Add person")
|
|
print(" people note <name> <text> - Add note")
|
|
print(" people show <name> - Show person")
|
|
|
|
if __name__ == "__main__":
|
|
main()
|