mirror of
https://github.com/muratcankoylan/Agent-Skills-for-Context-Engineering.git
synced 2026-09-14 20:36:54 +08:00
ace9c0aeb3
- Personal operating system for founders/creators as Claude Code skill - 6 isolated modules following context engineering principles - 4 automation scripts with tool-design consolidation - Complete skills traceability documentation
140 lines
3.8 KiB
Python
140 lines
3.8 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Stale Contacts Finder
|
|
Identifies contacts that haven't been reached out to recently.
|
|
"""
|
|
|
|
import json
|
|
from datetime import datetime, timedelta
|
|
from pathlib import Path
|
|
|
|
BRAIN_ROOT = Path(__file__).parent.parent.parent
|
|
|
|
# Thresholds by circle (in days)
|
|
THRESHOLDS = {
|
|
'inner': 14, # 2 weeks
|
|
'active': 30, # 1 month
|
|
'network': 60, # 2 months
|
|
'dormant': 180 # 6 months (for potential reactivation)
|
|
}
|
|
|
|
def load_jsonl(filepath):
|
|
"""Load JSONL file, skipping schema lines."""
|
|
items = []
|
|
if not filepath.exists():
|
|
return items
|
|
with open(filepath, 'r') as f:
|
|
for line in f:
|
|
line = line.strip()
|
|
if not line:
|
|
continue
|
|
try:
|
|
data = json.loads(line)
|
|
if '_schema' not in data:
|
|
items.append(data)
|
|
except json.JSONDecodeError:
|
|
continue
|
|
return items
|
|
|
|
def days_since(date_str):
|
|
"""Calculate days since a date string."""
|
|
if not date_str:
|
|
return 999 # Very stale if no date
|
|
try:
|
|
date = datetime.fromisoformat(date_str.replace('Z', '+00:00'))
|
|
return (datetime.now(date.tzinfo) - date).days
|
|
except (ValueError, TypeError):
|
|
return 999
|
|
|
|
def find_stale_contacts():
|
|
"""Find contacts needing outreach."""
|
|
contacts = load_jsonl(BRAIN_ROOT / 'network' / 'contacts.jsonl')
|
|
|
|
stale = {
|
|
'urgent': [], # Way overdue
|
|
'due': [], # Due for contact
|
|
'coming_up': [] # Getting close
|
|
}
|
|
|
|
for contact in contacts:
|
|
circle = contact.get('circle', 'network')
|
|
threshold = THRESHOLDS.get(circle, 60)
|
|
days = days_since(contact.get('last_contact'))
|
|
|
|
contact_info = {
|
|
'name': contact.get('name', 'Unknown'),
|
|
'circle': circle,
|
|
'days_since': days,
|
|
'threshold': threshold,
|
|
'handle': contact.get('handle', ''),
|
|
'notes': contact.get('notes', '')[:100]
|
|
}
|
|
|
|
if days > threshold * 1.5:
|
|
stale['urgent'].append(contact_info)
|
|
elif days > threshold:
|
|
stale['due'].append(contact_info)
|
|
elif days > threshold * 0.75:
|
|
stale['coming_up'].append(contact_info)
|
|
|
|
return stale
|
|
|
|
def generate_report():
|
|
"""Generate stale contacts report."""
|
|
stale = find_stale_contacts()
|
|
|
|
output = f"""
|
|
# Stale Contacts Report
|
|
Generated: {datetime.now().isoformat()}
|
|
|
|
## Urgently Overdue ({len(stale['urgent'])})
|
|
"""
|
|
|
|
if stale['urgent']:
|
|
for c in sorted(stale['urgent'], key=lambda x: -x['days_since']):
|
|
output += f"- **{c['name']}** ({c['circle']}) - {c['days_since']} days since contact\n"
|
|
if c['handle']:
|
|
output += f" {c['handle']}\n"
|
|
else:
|
|
output += "None! You're on top of things.\n"
|
|
|
|
output += f"""
|
|
## Due for Contact ({len(stale['due'])})
|
|
"""
|
|
|
|
if stale['due']:
|
|
for c in sorted(stale['due'], key=lambda x: -x['days_since']):
|
|
output += f"- {c['name']} ({c['circle']}) - {c['days_since']} days\n"
|
|
else:
|
|
output += "None due right now.\n"
|
|
|
|
output += f"""
|
|
## Coming Up ({len(stale['coming_up'])})
|
|
"""
|
|
|
|
if stale['coming_up']:
|
|
for c in sorted(stale['coming_up'], key=lambda x: -x['days_since']):
|
|
output += f"- {c['name']} ({c['circle']}) - {c['days_since']} days (threshold: {c['threshold']})\n"
|
|
else:
|
|
output += "No contacts approaching threshold.\n"
|
|
|
|
output += """
|
|
## Suggested Actions
|
|
|
|
1. Send a quick "thinking of you" message to urgent contacts
|
|
2. Schedule calls with due inner-circle contacts
|
|
3. Engage with content from coming-up contacts
|
|
|
|
## Thresholds
|
|
|
|
- Inner circle: Every 2 weeks
|
|
- Active: Every month
|
|
- Network: Every 2 months
|
|
- Dormant: Quarterly check for reactivation
|
|
"""
|
|
|
|
return output
|
|
|
|
if __name__ == '__main__':
|
|
print(generate_report())
|