CLI scripts for exporting and analyzing Google Calendar events.
pnpm calendar:scrape— Fetch the last N days (default 90) of events from the configured calendar and write JSON under.data/calendar-audit/<calendar-id>/<YYYY-MM-DD>/events.json.pnpm calendar:analyze— Read the latest audit JSON and emit grouped hours by week tocalendar-hours.csvandcalendar-hours.json(both gitignored). Events are grouped by client categories defined inclients.json, then by title inside each client bucket.
Environment (set in .env.local):
GOOGLE_CALENDAR_ID(or pass--calendar)CALENDAR_AUDIT_DAYS(optional override of 90)GOOGLE_CLIENT_SECRET_JSON(preferred) orGOOGLE_CLIENT_ID/GOOGLE_CLIENT_SECRET- Optional:
GOOGLE_REDIRECT_URIS(comma-separated),GOOGLE_PROJECT_ID
First run prompts OAuth consent and caches a token in .data/token.json; credentials JSON is read from .data/credentials.json (auto-generated from env if missing).
# Scrape events
pnpm calendar:scrape -- --calendar "<id-or-name>"
# Analyze latest scrape (weekly totals by title)
pnpm calendar:analyzeCreate a clients.json at repo root to map client names to alias keywords used for matching event titles. Start from the provided template:
cp clients.example.json clients.jsonThen edit clients.json with your own categories and aliases. Example:
{
"Northwind Ventures": ["northwind", "nv"],
"Beacon Outreach": ["beacon"],
"Harper Nolan": ["harper", "hn"]
}Rules:
- Matching is case-insensitive against event
summary; aliases and the client key name are both used. - Order matters: the first client whose key/aliases match an event claims it.
- Events with no match are grouped under
Other. - CSV output columns:
week_start, client, hours, count(newest weeks first). CSV does not list individual event names. - JSON output structure:
{ weeks: [{ weekStart, clients: [{ client, hours, count, summaries: [{ summary, hours, count }] }] }] }and includes per-event-title rollups to help find missing keywords (e.g., items falling intoOther).