Files
claude-code/skills/gcal/scripts/agenda.py
OpenCode Test 05d1fa41ba Add hooks and refactor skills to use resources pattern
Phase 1 of plugin-structure refactor:

- Add hooks/hooks.json for SessionStart automation
- Refactor gmail skill:
  - Extract inline scripts to scripts/check_unread.py, check_urgent.py, search.py
  - Add references/query-patterns.md for query documentation
  - Simplify SKILL.md to reference scripts instead of inline code
- Add gcal/scripts/agenda.py for direct calendar access
- Make all scripts executable

This follows the "Skill with Bundled Resources" pattern from
developing-claude-code-plugins best practices.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 02:33:10 -08:00

105 lines
3.3 KiB
Python
Executable File

#!/usr/bin/env python3
"""Get calendar agenda for a time range."""
import os
import sys
from datetime import datetime, timedelta
# Set credentials path
os.environ.setdefault('GMAIL_CREDENTIALS_PATH', os.path.expanduser('~/.gmail-mcp/credentials.json'))
from gmail_mcp.utils.GCP.gmail_auth import get_calendar_service
def format_event(event):
"""Format a single event for display."""
start = event['start'].get('dateTime', event['start'].get('date'))
end = event['end'].get('dateTime', event['end'].get('date'))
# Parse datetime
if 'T' in start:
start_dt = datetime.fromisoformat(start.replace('Z', '+00:00'))
end_dt = datetime.fromisoformat(end.replace('Z', '+00:00'))
time_str = start_dt.strftime('%I:%M %p').lstrip('0')
duration = end_dt - start_dt
if duration.seconds >= 3600:
dur_str = f"{duration.seconds // 3600}h"
else:
dur_str = f"{duration.seconds // 60}m"
else:
time_str = "All day"
dur_str = ""
summary = event.get('summary', '(No title)')
location = event.get('location', '')
line = f" {time_str:>10} {summary}"
if dur_str:
line += f" ({dur_str})"
if location:
line += f"\n 📍 {location}"
return line
def main():
mode = sys.argv[1] if len(sys.argv) > 1 else 'today'
service = get_calendar_service()
now = datetime.utcnow()
if mode == 'today':
start = now.replace(hour=0, minute=0, second=0, microsecond=0)
end = start + timedelta(days=1)
title = f"Today — {start.strftime('%A, %b %d')}"
elif mode == 'tomorrow':
start = (now + timedelta(days=1)).replace(hour=0, minute=0, second=0, microsecond=0)
end = start + timedelta(days=1)
title = f"Tomorrow — {start.strftime('%A, %b %d')}"
elif mode == 'week':
start = now.replace(hour=0, minute=0, second=0, microsecond=0)
end = start + timedelta(days=7)
title = f"This Week — {start.strftime('%b %d')}-{end.strftime('%d')}"
else:
start = now
end = now + timedelta(days=7)
title = "Upcoming"
events_result = service.events().list(
calendarId='primary',
timeMin=start.isoformat() + 'Z',
timeMax=end.isoformat() + 'Z',
singleEvents=True,
orderBy='startTime',
maxResults=50
).execute()
events = events_result.get('items', [])
print(f"📅 {title}\n")
if not events:
print("No events scheduled.")
return
if mode == 'week':
# Group by day
by_day = {}
for event in events:
start_str = event['start'].get('dateTime', event['start'].get('date'))
day = start_str[:10]
if day not in by_day:
by_day[day] = []
by_day[day].append(event)
for day, day_events in sorted(by_day.items()):
day_dt = datetime.fromisoformat(day)
print(f"━━━ {day_dt.strftime('%A, %b %d')} ━━━")
for event in day_events:
print(format_event(event))
print()
else:
for event in events:
print(format_event(event))
print("\nNo more events" + (" today." if mode == 'today' else "."))
if __name__ == '__main__':
main()