Files
wehub-resource-sync bb5c75ce05
Component Security Validation / Security Audit (push) Has been cancelled
Deploy to Cloudflare Pages / deploy (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:38:58 +08:00

306 lines
12 KiB
Python

#!/usr/bin/env python3
"""
Daily Notes Connectivity Agent
Analyzes daily notes and creates meaningful connections between them and other vault content.
"""
import os
import re
import yaml
from datetime import datetime, timedelta
from pathlib import Path
from collections import defaultdict
import json
class DailyNotesConnector:
def __init__(self, vault_path):
self.vault_path = Path(vault_path)
self.connections_made = 0
self.notes_processed = 0
self.patterns = {
'project': r'(?:project|AI IDEAS|idea|experiment|build|develop)',
'meeting': r'(?:meeting|call|discussion|client|consultation)',
'technical': r'(?:MCP|LangChain|GraphRAG|AI|ML|model|agent|tool)',
'client': r'(?:client|consulting|business|CamRohn)',
'personal': r'(?:family|personal|reflection|stoic|goal)',
'research': r'(?:research|paper|study|article|documentation)',
'community': r'(?:Austin|LangChain|meetup|community|conference)'
}
self.connection_map = defaultdict(list)
def find_daily_notes(self):
"""Find all daily notes across the vault."""
daily_notes = []
# Search patterns for daily notes
patterns = [
self.vault_path / "Daily Notes" / "*.md",
self.vault_path / "REMOTE_VAULT01" / "Daily Notes" / "*.md",
self.vault_path / "Daily Email" / "*.md",
self.vault_path / "_PERSONAL_" / "JOURNAL" / "**" / "*.md"
]
for pattern in patterns:
for file_path in self.vault_path.glob(str(pattern).split(str(self.vault_path) + "/")[1]):
# Check if filename matches date pattern
if re.match(r'\d{4}-\d{2}-\d{2}', file_path.stem):
daily_notes.append(file_path)
return sorted(daily_notes)
def extract_frontmatter(self, file_path):
"""Extract frontmatter from a markdown file."""
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
if content.startswith('---'):
try:
end_index = content.index('---', 3)
frontmatter_text = content[3:end_index].strip()
return yaml.safe_load(frontmatter_text), content[end_index+3:]
except:
return {}, content
return {}, content
def update_frontmatter(self, file_path, frontmatter, body):
"""Update the frontmatter of a file."""
yaml_content = yaml.dump(frontmatter, default_flow_style=False, allow_unicode=True)
new_content = f"---\n{yaml_content}---\n{body}"
with open(file_path, 'w', encoding='utf-8') as f:
f.write(new_content)
def analyze_content(self, content):
"""Analyze content to identify topics and themes."""
content_lower = content.lower()
topics = defaultdict(int)
for topic, pattern in self.patterns.items():
matches = re.findall(pattern, content_lower)
topics[topic] = len(matches)
# Extract specific mentions
mentions = {
'projects': re.findall(r'\[\[([^]]+)\]\]', content),
'headers': re.findall(r'^#+\s+(.+)$', content, re.MULTILINE),
'urls': re.findall(r'https?://[^\s\]]+', content),
'tags': re.findall(r'#(\w+)', content)
}
return topics, mentions
def find_related_content(self, topics, mentions, current_file):
"""Find related content based on topics and mentions."""
related = []
# Map topics to vault directories
topic_dirs = {
'project': ['AI IDEAS', 'AI Development'],
'meeting': ['CamRohn LLC/Client Work', 'Austin LangChain'],
'technical': ['AI Development', 'Model Context Protocol (MCP)'],
'client': ['CamRohn LLC', 'Second Opinion DDS'],
'research': ['AI Articles and Research', 'Clippings'],
'community': ['Austin LangChain', 'AI Conferences and Competitions']
}
# Find files based on dominant topics
for topic, count in sorted(topics.items(), key=lambda x: x[1], reverse=True):
if count > 0 and topic in topic_dirs:
for dir_name in topic_dirs[topic]:
dir_path = self.vault_path / dir_name
if dir_path.exists():
# Add MOC if exists
moc_path = dir_path / f"MOC - {dir_name.split('/')[-1]}.md"
if moc_path.exists():
related.append((moc_path, f"{topic} reference"))
# Add specific mentioned files
for mention in mentions['projects']:
if dir_name in mention:
file_path = self.vault_path / f"{mention}.md"
if file_path.exists() and file_path != current_file:
related.append((file_path, "direct mention"))
return related[:10] # Limit to top 10 connections
def find_temporal_connections(self, file_path, all_notes):
"""Find temporal connections (previous/next days, weekly summaries)."""
temporal = []
# Extract date from filename
date_match = re.match(r'(\d{4})-(\d{2})-(\d{2})', file_path.stem)
if not date_match:
return temporal
current_date = datetime(int(date_match.group(1)),
int(date_match.group(2)),
int(date_match.group(3)))
# Find previous and next days
for days_offset in [-1, 1]:
target_date = current_date + timedelta(days=days_offset)
target_str = target_date.strftime('%Y-%m-%d')
for note in all_notes:
if target_str in note.stem:
temporal.append((note, f"{'Previous' if days_offset < 0 else 'Next'} day"))
break
# Find weekly connections (same week)
week_start = current_date - timedelta(days=current_date.weekday())
week_end = week_start + timedelta(days=6)
for note in all_notes:
date_match = re.match(r'(\d{4})-(\d{2})-(\d{2})', note.stem)
if date_match:
note_date = datetime(int(date_match.group(1)),
int(date_match.group(2)),
int(date_match.group(3)))
if week_start <= note_date <= week_end and note != file_path:
temporal.append((note, "Same week"))
return temporal
def process_daily_note(self, file_path, all_notes):
"""Process a single daily note and add connections."""
print(f"Processing: {file_path.relative_to(self.vault_path)}")
frontmatter, body = self.extract_frontmatter(file_path)
topics, mentions = self.analyze_content(body)
# Find related content
content_related = self.find_related_content(topics, mentions, file_path)
temporal_related = self.find_temporal_connections(file_path, all_notes)
# Build related list
new_related = []
# Add temporal connections first
for related_file, relation_type in temporal_related:
if "Previous" in relation_type or "Next" in relation_type:
relative_path = related_file.relative_to(self.vault_path)
link = f"[[{relative_path.with_suffix('').as_posix()}]]"
if relation_type == "Previous day":
new_related.insert(0, f"{link} # {relation_type}")
else:
new_related.append(f"{link} # {relation_type}")
# Add content-based connections
for related_file, relation_type in content_related:
relative_path = related_file.relative_to(self.vault_path)
link = f"[[{relative_path.with_suffix('').as_posix()}]]"
comment = f" # {relation_type.title()}"
new_related.append(f"{link}{comment}")
# Update frontmatter if we found new connections
if new_related:
existing_related = frontmatter.get('related', [])
if isinstance(existing_related, list):
# Merge and deduplicate - convert lists to strings for deduplication
combined = existing_related + new_related
seen = set()
all_related = []
for item in combined:
if item not in seen:
seen.add(item)
all_related.append(item)
else:
all_related = new_related
frontmatter['related'] = all_related
self.update_frontmatter(file_path, frontmatter, body)
self.connections_made += len(new_related)
self.notes_processed += 1
# Track patterns for reporting
for topic, count in topics.items():
if count > 0:
self.connection_map[topic].append(file_path.stem)
def generate_report(self):
"""Generate a report of connections made."""
report = f"""# Daily Notes Connectivity Report
Generated: {datetime.now().strftime('%Y-%m-%d %H:%M')}
## Summary
- Total daily notes processed: {self.notes_processed}
- Total connections created: {self.connections_made}
- Average connections per note: {self.connections_made / max(self.notes_processed, 1):.1f}
## Connection Patterns Discovered
"""
for topic, dates in self.connection_map.items():
if dates:
report += f"### {topic.title()} Topics\n"
report += f"Found in {len(dates)} daily notes:\n"
# Show recent examples
for date in sorted(dates)[-5:]:
report += f"- [[{date}]]\n"
report += "\n"
report += """## Themes Across Time Periods
### Recent Trends (Last 30 days)
"""
# Analyze recent trends
recent_date = datetime.now() - timedelta(days=30)
recent_topics = defaultdict(int)
for topic, dates in self.connection_map.items():
for date_str in dates:
try:
date_match = re.match(r'(\d{4})-(\d{2})-(\d{2})', date_str)
if date_match:
note_date = datetime(int(date_match.group(1)),
int(date_match.group(2)),
int(date_match.group(3)))
if note_date >= recent_date:
recent_topics[topic] += 1
except:
pass
for topic, count in sorted(recent_topics.items(), key=lambda x: x[1], reverse=True):
report += f"- **{topic.title()}**: {count} occurrences\n"
report += "\n## Recommendations\n\n"
report += "1. Consider creating weekly/monthly summary notes to consolidate themes\n"
report += "2. Review orphaned daily notes that lack connections\n"
report += "3. Add more content to empty daily notes for better connectivity\n"
return report
def run(self):
"""Main execution method."""
print("Daily Notes Connectivity Agent Starting...")
print(f"Vault path: {self.vault_path}")
# Find all daily notes
daily_notes = self.find_daily_notes()
print(f"Found {len(daily_notes)} daily notes")
# Process each note
for note in daily_notes:
try:
self.process_daily_note(note, daily_notes)
except Exception as e:
print(f"Error processing {note}: {e}")
# Generate and save report
report = self.generate_report()
report_path = self.vault_path / "System_Files" / "Daily_Notes_Connectivity_Report.md"
with open(report_path, 'w', encoding='utf-8') as f:
f.write(report)
print(f"\nComplete! Report saved to: {report_path}")
print(f"Processed {self.notes_processed} notes, created {self.connections_made} connections")
if __name__ == "__main__":
vault_path = "/Users/cam/VAULT01"
connector = DailyNotesConnector(vault_path)
connector.run()