Skip to content

📅 feat(calendar): Add Google Calendar API integration (v3.3.0)#30

Merged
AojdevStudio merged 5 commits intomainfrom
feature/calendar-integration
Jan 9, 2026
Merged

📅 feat(calendar): Add Google Calendar API integration (v3.3.0)#30
AojdevStudio merged 5 commits intomainfrom
feature/calendar-integration

Conversation

@AojdevStudio
Copy link
Owner

@AojdevStudio AojdevStudio commented Jan 9, 2026

Summary

  • Complete Google Calendar API integration with 9 operations
  • PAI contact name resolution (e.g., "Mary" → email)
  • Google Meet link auto-generation for events
  • RRULE recurrence support
  • Redis caching with proper pattern-based invalidation

Operations

Operation Description
listCalendars List user's calendars
getCalendar Get calendar details
listEvents List events with time filtering
getEvent Get event details
createEvent Create with attendees, recurrence, Google Meet
updateEvent Partial updates
deleteEvent Delete with notification options
quickAdd Natural language event creation
checkFreeBusy Check availability

Test plan

  • 59 unit tests passing
  • TypeScript build passes
  • MCP integration tested (listCalendars, listEvents, checkFreeBusy verified)
  • Docker container rebuilt and verified
  • Cache invalidation bug fixed (P1)

Files changed

  • 20 files, +5,539 lines
  • New calendar module at src/modules/calendar/
  • Server integration in index.ts
  • Tool discovery in src/tools/listTools.ts

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added Google Calendar integration with nine calendar operations: browse calendars and events, create/update/delete events, quick event creation, and check availability
    • Integrated contact name resolution for event attendees
    • Full support for recurring events, timezones, and video conference data
  • Documentation

    • Added comprehensive Calendar integration specification and changelog entry

✏️ Tip: You can customize this high-level summary in your review settings.

…ns (v3.3.0)

Add complete Calendar module following operation-based architecture:

Operations:
- listCalendars, getCalendar - Calendar management
- listEvents, getEvent - Event read operations
- createEvent, updateEvent, deleteEvent - Full CRUD
- quickAdd - Natural language event creation
- checkFreeBusy - Availability checking

Features:
- PAI contact resolution (resolve names like "Mary" to emails)
- Redis caching (5-min for reads, 60s for freebusy)
- Full performance monitoring integration
- 59 unit tests across 4 test suites

New OAuth scopes required (re-authentication needed):
- calendar.readonly - Read calendars and events
- calendar.events - Full event CRUD

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@coderabbitai
Copy link

coderabbitai bot commented Jan 9, 2026

Warning

Rate limit exceeded

@AojdevStudio has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 27 minutes and 9 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between ca9e58d and dbd3332.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (8)
  • .env.example
  • CHANGELOG.md
  • src/modules/calendar/__tests__/contacts.test.ts
  • src/modules/calendar/__tests__/list.test.ts
  • src/modules/calendar/contacts.ts
  • src/modules/calendar/create.ts
  • src/modules/calendar/update.ts
  • src/modules/calendar/utils.ts
📝 Walkthrough

Walkthrough

This PR introduces a comprehensive Google Calendar API integration (v3.3.0) with nine calendar operations (listCalendars, getCalendar, listEvents, getEvent, createEvent, updateEvent, deleteEvent, quickAdd, checkFreeBusy), PAI contact resolution, Redis caching, performance monitoring, and extensive test coverage across new calendar module files and integration points.

Changes

Cohort / File(s) Summary
Documentation & Specification
CHANGELOG.md, CLAUDE.md, specs/google-calendar-integration.md
Added changelog entry for v3.3.0 with calendar integration details; updated project documentation with Calendar API integration overview, architecture notes, and MCP operations; introduced comprehensive 1502-line integration specification covering architecture, data models, OAuth scopes, caching strategy, testing plan, and implementation guidance.
Configuration & Metadata
Dockerfile, package.json
Added comment clarifying that *_PATH environment variables are file paths (not secrets) in Dockerfile; bumped version from 3.2.0 to 3.3.0 and updated package description in package.json.
Main Integration Wiring
index.ts, src/modules/types.ts, src/tools/listTools.ts
Extended index.ts with calendar client initialization, tool discovery/dispatch logic for 9 calendar operations, OAuth scope updates (calendar.readonly, calendar.events), and type imports; added CalendarContext interface to src/modules/types.ts; added calendar module tool structure with 9 operations to listTools.ts.
Calendar Module Types
src/modules/calendar/types.ts
Introduced 24+ TypeScript interfaces for calendar operations, including options types (ListCalendarsOptions, ListEventsOptions, etc.), result types (ListCalendarsResult, EventResult, etc.), and supporting types (Attendee, EventDateTime, ConferenceData, FreeBusyResult, ContactEntry).
Calendar Module - List Operations
src/modules/calendar/list.ts
Implemented listCalendars and listEvents functions with pagination, caching (5-minute TTL), performance monitoring, and API limit compliance; supports optional fields, nextPageToken handling, and per-calendar result mapping.
Calendar Module - Read Operations
src/modules/calendar/read.ts
Implemented getCalendar and getEvent functions with caching, performance tracking, and comprehensive event data mapping (creator, organizer, attendees, conference data, attachments, reminders); includes parseAttendees helper.
Calendar Module - Create Operations
src/modules/calendar/create.ts
Implemented createEvent and quickAdd functions with attendee resolution, event time validation, conference data handling, cache invalidation, and performance logging; supports RRULE, attachments, reminders, and custom visibility.
Calendar Module - Update & Delete
src/modules/calendar/update.ts, src/modules/calendar/delete.ts
Implemented updateEvent with partial PATCH-style updates, attendee resolution, and cache invalidation; implemented deleteEvent for permanent event deletion with cache and performance tracking.
Calendar Module - Advanced Operations
src/modules/calendar/freebusy.ts
Implemented checkFreeBusy function for querying availability across multiple calendars with short TTL caching and per-calendar error handling.
Calendar Module - Contact Resolution
src/modules/calendar/contacts.ts
Implemented PAI contact resolution via resolveContacts and parseContactsFile functions; supports case-insensitive name matching, email passthrough, file loading from PAI_CONTACTS_PATH, and error reporting with available contacts listing.
Calendar Module - Public Exports
src/modules/calendar/index.ts
Created centralized module aggregator re-exporting 24+ types and 9 operation functions (listCalendars, listEvents, getCalendar, getEvent, createEvent, updateEvent, deleteEvent, quickAdd, checkFreeBusy, resolveContacts).
Calendar Module Tests
src/modules/calendar/__tests__/list.test.ts, src/modules/calendar/__tests__/read.test.ts, src/modules/calendar/__tests__/freebusy.test.ts, src/modules/calendar/__tests__/contacts.test.ts
Added 59 unit tests across 4 test suites: list operations (pagination, caching, API limits), read operations (retrieval, data mapping, performance tracking), free/busy queries (timezone handling, error propagation), and contact resolution (name/email matching, file parsing, error handling).

Sequence Diagram(s)

sequenceDiagram
    participant Client as MCP Client
    participant Handler as Tool Handler
    participant Calendar as Calendar Module
    participant GoogleAPI as Google Calendar API
    participant Cache as Cache Manager
    participant Perf as Performance Monitor

    Client->>Handler: callTool(calendarId, operation, options)
    Handler->>Calendar: operation(options, context)
    
    alt Cache Hit
        Calendar->>Cache: get(cacheKey)
        Cache-->>Calendar: cachedResult
        Calendar-->>Handler: result
    else Cache Miss
        Calendar->>GoogleAPI: call(params)
        GoogleAPI-->>Calendar: apiResponse
        Calendar->>Cache: set(cacheKey, result, ttl)
        Calendar->>Perf: track(operationName, duration)
        Calendar-->>Handler: result
    end
    
    Handler-->>Client: success(result)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

  • PR #27: Introduced the operation-based progressive-disclosure architecture that this PR extends by adding a new calendar module with 9 operations wired into the same index.ts tool dispatch and listTools structure.
  • PR #29: Added another Google API module and directly modified the same integration points (index.ts tool registration/dispatch, OAuth scopes, and src/modules/types.ts Context extensions).

Poem

🐰 A calendar so bright and new,
With contacts resolved and caches too!
Nine operations, all in a row,
Watch the rabbit's scheduling flow! ✨📅

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically identifies the main feature being added: Google Calendar API integration with version context, directly matching the substantial changes throughout the PR.
Docstring Coverage ✅ Passed Docstring coverage is 94.74% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

- Add Git Workflow section noting main branch is protected
- Add Google Calendar API integration to Project Overview
- Add Calendar API Integration to Core Components
- Add Calendar Operations to Tools list

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@github-actions
Copy link

github-actions bot commented Jan 9, 2026

📊 Type Coverage Report

Type Coverage: 97.67%

This PR's TypeScript type coverage analysis is complete.
Check the full report in the workflow artifacts.

1 similar comment
@github-actions
Copy link

github-actions bot commented Jan 9, 2026

📊 Type Coverage Report

Type Coverage: 97.67%

This PR's TypeScript type coverage analysis is complete.
Check the full report in the workflow artifacts.

@github-actions
Copy link

github-actions bot commented Jan 9, 2026

Performance Comparison Report

Operation Performance

Operation Baseline Avg Current Avg Change Status
listFiles 95.0ms 46.8ms -50.7% 🚀 IMPROVEMENT
readFile 180.0ms 106.5ms -40.8% 🚀 IMPROVEMENT
createFile 250.0ms 145.4ms -41.9% 🚀 IMPROVEMENT
cacheOperation 45.0ms 48.0ms 6.6% ✅ OK

Memory Usage

  • Baseline: 45.2 MB
  • Current: 4.41 MB
  • Change: -90.2%

Summary

  • 🚀 Improvements: 3
  • ❌ Regressions: 0

Performance report generated by Claude Code

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
index.ts (2)

385-396: Update server version to match release (PR says v3.3.0, code still 3.1.0).
The MCP server advertises version: "3.1.0" (Line 388-389) but this PR is labeled v3.3.0; that mismatch will confuse clients and support/debug flows.


590-607: Avoid logging raw params at info-level (likely PII).
logger.info('Tool called', { ..., params }) (Line 593) will capture attendee emails, event descriptions, etc. Consider redacting known sensitive fields or logging only shape/keys at info-level.

🤖 Fix all issues with AI agents
In @CHANGELOG.md:
- Around line 31-36: The fenced code block in the CHANGELOG.md that lists the
OAuth scopes (the block containing "calendar.readonly    - Read calendars and
events" and "calendar.events      - Create, update, delete events") is missing a
language identifier; update the opening fence to include a language (e.g.,
change ``` to ```text) so markdownlint MD040 is satisfied and the block is
properly annotated.

In @src/modules/calendar/contacts.ts:
- Around line 15-20: DEFAULT_CONTACTS_PATH is a hardcoded user-specific absolute
path and should be replaced with a safe, non-user-specific default (e.g.,
relative "./contacts.md") while honoring process.env.PAI_CONTACTS_PATH if set;
update the code that references DEFAULT_CONTACTS_PATH and ensure missing files
are handled gracefully (no leak/throw). Also memoize loadContacts() by adding a
module-level cache (e.g., cachedContacts) so subsequent resolveContacts() calls
reuse the parsed contacts instead of re-reading the file each time; invalidate
or refresh the cache only when PAI_CONTACTS_PATH changes or provide an explicit
reload function.
- Around line 44-47: The for-loop in contacts.ts uses a single-line if without
braces causing an ESLint "curly" error; update the block inside the loop (the
for (const line of lines) loop that defines const trimmed) to wrap the
conditional body in braces — i.e., replace the single-line "if (!trimmed)
continue;" with a braced statement so the continue is inside { } to satisfy the
curly rule.

In @src/modules/calendar/create.ts:
- Around line 25-56: The duplicate validateEventTimes implementation should be
extracted into a shared utility and imported where needed: move the current
validateEventTimes function into a new shared module (exporting function
validateEventTimes(start, end)), remove the duplicate implementations from
create.ts and update.ts, and replace them with imports that call the shared
validateEventTimes; ensure the exported function signature and error behavior
remain identical so existing callers (e.g., where createEvent or updateEvent
call validateEventTimes) need only swap the local function for the import.
- Around line 61-102: The parseAttendees implementation is duplicated in
create.ts and update.ts; move it into the shared calendar utils alongside
validateEventTimes by creating a single exported function
parseAttendees(attendees: calendar_v3.Schema$EventAttendee[] | undefined):
Attendee[] | undefined in src/modules/calendar/utils.ts, importing necessary
types (calendar_v3 and Attendee) in that file, then replace the duplicate
implementations in create.ts and update.ts with an import of the shared
parseAttendees and call it; ensure function name and signature stay identical so
callers need only the import change.

In @src/modules/calendar/freebusy.ts:
- Around line 39-48: The cache key uses JSON.stringify(options) which can
produce different strings for the same logical query when object property order
differs; change the key generation in the calendar:checkFreeBusy path (where
cacheKey is defined and context.cacheManager.get is called) to use a
stable/canonical serialization of options (e.g., sort object keys before
stringifying or build the key from explicit known fields such as timeMin,
timeMax, calendarId, attendees) so identical queries always produce the same
cacheKey; keep the rest of the flow (context.performanceMonitor.track and
returning cached as FreeBusyResult) unchanged.

In @src/modules/calendar/read.ts:
- Around line 146-154: getEvent's cache key currently only uses eventId which
can collide across calendars; update the cacheKey construction in getEvent to
include calendarId (e.g., `calendar:getEvent:${calendarId}:${eventId}`) and
ensure any corresponding cache writes use the same composite key when calling
context.cacheManager.set, so that context.cacheManager.get/cache misses are
consistent and results don't bleed between calendars; keep references to
cacheKey, calendarId, eventId, getEvent, context.cacheManager.get and
context.cacheManager.set.

In @src/modules/calendar/update.ts:
- Around line 157-160: The current update logic only calls validateEventTimes
when both updates.start and updates.end are present, which misses partial
updates; modify the update handler around validateEventTimes to, when only one
of updates.start or updates.end is provided, fetch the existing event via
context.calendar.events.get using calendarId and eventId, derive finalStart =
updates.start || existing.data.start and finalEnd = updates.end ||
existing.data.end, and call validateEventTimes(finalStart, finalEnd) if both are
present to ensure start ≤ end before sending the patch.
🧹 Nitpick comments (11)
src/tools/listTools.ts (1)

9-11: Update documentation to reflect all available modules.

The documentation comments mention only "drive, sheets, forms, docs" but the codebase now includes gmail (v3.2.0+) and calendar (v3.3.0+) modules. Update the comments to reflect the complete module list.

📝 Suggested documentation update
- * 1. Agent reads 'gdrive://tools' to see available modules (drive, sheets, forms, docs)
+ * 1. Agent reads 'gdrive://tools' to see available modules (drive, sheets, forms, docs, gmail, calendar)
  * 2. Agent explores specific modules to see available operations
  * 3. Agent reads detailed documentation for specific operations
src/modules/calendar/freebusy.ts (1)

67-95: Consider more defensive handling of optional API response fields.

The code uses non-null assertions (!) extensively (lines 68-69, 80-81, 89-90) when transforming the API response. While the Google Calendar API likely guarantees these fields for successful responses, more defensive coding would provide better error messages if the API contract changes.

🛡️ More defensive transformation pattern
  const result: FreeBusyResult = {
-   timeMin: response.data.timeMin!,
-   timeMax: response.data.timeMax!,
+   timeMin: response.data.timeMin || options.timeMin,
+   timeMax: response.data.timeMax || options.timeMax,
    calendars: {},
  };

  // Transform response calendars to our type format
  if (response.data.calendars) {
    for (const [calendarId, calendarData] of Object.entries(
      response.data.calendars
    )) {
      result.calendars[calendarId] = {
        busy: (calendarData.busy || []).map((period) => ({
-         start: period.start!,
-         end: period.end!,
+         start: period.start || '',
+         end: period.end || '',
        })),
      };

This provides fallback values while maintaining type safety.

src/modules/calendar/__tests__/freebusy.test.ts (2)

15-45: Reduce @ts-expect-error/double-casts in mocks to keep tests honest under exactOptionalPropertyTypes.
Right now the context/calendar mocks rely on as unknown as plus multiple @ts-expect-error (Line 21, 33-38). Consider switching to a Partial<CalendarContext> / DeepPartial-style mock and only casting at the boundary once.


201-233: Nice coverage on cache-write behavior; consider asserting the cached-return path too.
You verify cacheManager.set is called (Line 229-232), but there’s no test that a subsequent call returns from cacheManager.get and skips calendar.freebusy.query.

src/modules/calendar/__tests__/list.test.ts (2)

8-39: Prefer typed CalendarContext (or Partial<CalendarContext>) over any in tests.
Using any for mockContext/mockCalendarApi (Line 9-38, 206-235) can hide breaking changes in the module contract (e.g., renamed logger methods, cacheManager shape).

Also applies to: 205-236


365-376: Cache-hit test for listEvents should also assert perf tracking (symmetry with listCalendars).
listCalendars cache-hit asserts performanceMonitor.track (Line 147-150), but listEvents cache-hit doesn’t (Line 365-375).

src/modules/calendar/list.ts (2)

42-48: Make cache keys deterministic (avoid JSON.stringify(options) directly).
listCalendars uses JSON.stringify(options) (Line 43), which can miss cache hits if callers construct objects with different key insertion orders; listEvents already avoids this by stringifying a constructed object (Line 149).

Also applies to: 148-154


62-109: Add error logging (and perf tracking on failure) around Calendar API calls.
Unlike checkFreeBusy, this module doesn’t log/rethrow with context if calendarList.list / events.list fails (Line 62, 181), which makes prod debugging harder.

Also applies to: 181-251

src/modules/calendar/read.ts (1)

88-122: Harden attendee parsing: don’t emit empty emails; preserve explicit boolean false when present.
email: attendee.email || '' (Line 95) can produce invalid attendees; and if (attendee.organizer) / if (attendee.self) drops false values (Line 110-118).

src/modules/calendar/index.ts (1)

8-62: Export surface looks clean and consistent with the tool wiring.
Optional: if parseContactsFile is meant to be a supported API (it’s exported from contacts.ts), consider re-exporting it here for consistency.

src/modules/calendar/create.ts (1)

277-281: Remove confusing comment with no implementation.

The comment mentions handling timeZone in params but contains no actual logic. This is misleading—timezone is correctly passed through the start/end EventDateTime objects in the eventResource. Remove this comment to avoid confusion.

🧹 Proposed cleanup
-  // Add timeZone to params if provided (for timezone-aware event creation)
-  if (timeZone) {
-    // TimeZone is set in the start/end EventDateTime objects, not at the params level
-    // So we just ensure it's passed through correctly in the eventResource
-  }
-
   const response = await context.calendar.events.insert(params);
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 947c661 and ca9e58d.

📒 Files selected for processing (21)
  • CHANGELOG.md
  • CLAUDE.md
  • Dockerfile
  • index.ts
  • package.json
  • specs/google-calendar-integration.md
  • src/modules/calendar/__tests__/contacts.test.ts
  • src/modules/calendar/__tests__/freebusy.test.ts
  • src/modules/calendar/__tests__/list.test.ts
  • src/modules/calendar/__tests__/read.test.ts
  • src/modules/calendar/contacts.ts
  • src/modules/calendar/create.ts
  • src/modules/calendar/delete.ts
  • src/modules/calendar/freebusy.ts
  • src/modules/calendar/index.ts
  • src/modules/calendar/list.ts
  • src/modules/calendar/read.ts
  • src/modules/calendar/types.ts
  • src/modules/calendar/update.ts
  • src/modules/types.ts
  • src/tools/listTools.ts
🧰 Additional context used
📓 Path-based instructions (7)
package.json

📄 CodeRabbit inference engine (CLAUDE.md)

Require Node.js 18+ for ES2022 support

Files:

  • package.json
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,js}: Use Winston-based logging with configurable levels, file rotation, separate error and combined log files, and console output for development
Implement Redis caching with 5-minute TTL for cached data, automatic invalidation on write operations, and graceful fallback when Redis is unavailable
Track real-time operation timing, statistics, memory usage, cache hit/miss ratios, and log performance metrics every 30 seconds
Support batch file operations (create, update, delete, move multiple files) in a single operation with optimized efficiency and comprehensive error handling per operation
Export Google Docs as Markdown, Google Sheets as CSV, Google Presentations as Plain text, Google Drawings as PNG, and encode binary files as Base64
Use Google Cloud local auth with OAuth2 for authentication and save credentials to .gdrive-server-credentials.json
Ensure ESLint compliance and resolve all ESLint violations for CI pipeline compatibility

Files:

  • src/modules/calendar/__tests__/freebusy.test.ts
  • src/modules/calendar/delete.ts
  • src/modules/calendar/freebusy.ts
  • src/modules/calendar/read.ts
  • src/modules/calendar/create.ts
  • src/modules/calendar/contacts.ts
  • src/modules/types.ts
  • src/modules/calendar/update.ts
  • src/modules/calendar/index.ts
  • src/modules/calendar/list.ts
  • src/tools/listTools.ts
  • src/modules/calendar/__tests__/list.test.ts
  • src/modules/calendar/types.ts
  • src/modules/calendar/__tests__/contacts.test.ts
  • src/modules/calendar/__tests__/read.test.ts
  • index.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Implement MCP Resources for listing and reading Google Drive files
Implement MCP Tools for read operations (search, read, listSheets, readSheet), write operations (createFile, updateFile, createFolder), Sheets operations, Forms operations, Docs operations, Gmail operations, batch operations, and enhanced search
Use StdioServerTransport for MCP communication

Files:

  • src/modules/calendar/__tests__/freebusy.test.ts
  • src/modules/calendar/delete.ts
  • src/modules/calendar/freebusy.ts
  • src/modules/calendar/read.ts
  • src/modules/calendar/create.ts
  • src/modules/calendar/contacts.ts
  • src/modules/types.ts
  • src/modules/calendar/update.ts
  • src/modules/calendar/index.ts
  • src/modules/calendar/list.ts
  • src/tools/listTools.ts
  • src/modules/calendar/__tests__/list.test.ts
  • src/modules/calendar/types.ts
  • src/modules/calendar/__tests__/contacts.test.ts
  • src/modules/calendar/__tests__/read.test.ts
  • index.ts
**/*.{test,spec}.{ts,tsx,js}

📄 CodeRabbit inference engine (CLAUDE.md)

Maintain Jest coverage thresholds and ensure test infrastructure compatibility with ESM/CommonJS

Files:

  • src/modules/calendar/__tests__/freebusy.test.ts
  • src/modules/calendar/__tests__/list.test.ts
  • src/modules/calendar/__tests__/contacts.test.ts
  • src/modules/calendar/__tests__/read.test.ts
**/*.ts

⚙️ CodeRabbit configuration file

**/*.ts: Review the TypeScript code for:

  • Type safety and correctness
  • Code quality and best practices
  • Performance issues
  • Security vulnerabilities
  • Logic errors

Files:

  • src/modules/calendar/__tests__/freebusy.test.ts
  • src/modules/calendar/delete.ts
  • src/modules/calendar/freebusy.ts
  • src/modules/calendar/read.ts
  • src/modules/calendar/create.ts
  • src/modules/calendar/contacts.ts
  • src/modules/types.ts
  • src/modules/calendar/update.ts
  • src/modules/calendar/index.ts
  • src/modules/calendar/list.ts
  • src/tools/listTools.ts
  • src/modules/calendar/__tests__/list.test.ts
  • src/modules/calendar/types.ts
  • src/modules/calendar/__tests__/contacts.test.ts
  • src/modules/calendar/__tests__/read.test.ts
  • index.ts
**/*.md

⚙️ CodeRabbit configuration file

**/*.md: Skip reviewing this markdown file as it contains documentation only.

Files:

  • specs/google-calendar-integration.md
  • CHANGELOG.md
  • CLAUDE.md
{Dockerfile,.dockerignore,tsconfig.json}

📄 CodeRabbit inference engine (CLAUDE.md)

Exclude test files from Docker images via .dockerignore and ensure TypeScript compilation excludes test files from production builds

Files:

  • Dockerfile
🧠 Learnings (4)
📚 Learning: 2025-12-23T20:37:40.210Z
Learnt from: CR
Repo: AojdevStudio/gdrive PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-23T20:37:40.210Z
Learning: Applies to **/*.{ts,tsx} : Implement MCP Resources for listing and reading Google Drive files

Applied to files:

  • package.json
  • src/modules/types.ts
  • src/modules/calendar/index.ts
📚 Learning: 2025-12-23T20:37:40.210Z
Learnt from: CR
Repo: AojdevStudio/gdrive PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-23T20:37:40.210Z
Learning: Applies to **/*.{ts,tsx} : Implement MCP Tools for read operations (search, read, listSheets, readSheet), write operations (createFile, updateFile, createFolder), Sheets operations, Forms operations, Docs operations, Gmail operations, batch operations, and enhanced search

Applied to files:

  • specs/google-calendar-integration.md
  • src/modules/types.ts
  • src/modules/calendar/index.ts
  • src/tools/listTools.ts
  • CLAUDE.md
  • index.ts
📚 Learning: 2025-12-23T20:37:40.210Z
Learnt from: CR
Repo: AojdevStudio/gdrive PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-23T20:37:40.210Z
Learning: Applies to **/*.{test,spec}.{ts,tsx,js} : Maintain Jest coverage thresholds and ensure test infrastructure compatibility with ESM/CommonJS

Applied to files:

  • src/modules/calendar/__tests__/list.test.ts
📚 Learning: 2025-12-23T20:37:40.210Z
Learnt from: CR
Repo: AojdevStudio/gdrive PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-23T20:37:40.210Z
Learning: Applies to **/*.{ts,tsx,js} : Use Google Cloud local auth with OAuth2 for authentication and save credentials to .gdrive-server-credentials.json

Applied to files:

  • Dockerfile
🧬 Code graph analysis (11)
src/modules/calendar/__tests__/freebusy.test.ts (3)
src/modules/types.ts (1)
  • CalendarContext (72-74)
src/modules/calendar/freebusy.ts (1)
  • checkFreeBusy (33-115)
src/modules/calendar/index.ts (1)
  • checkFreeBusy (59-59)
src/modules/calendar/freebusy.ts (3)
src/modules/calendar/index.ts (3)
  • checkFreeBusy (59-59)
  • FreeBusyOptions (36-36)
  • FreeBusyResult (37-37)
src/modules/calendar/types.ts (2)
  • FreeBusyOptions (298-303)
  • FreeBusyResult (308-321)
src/modules/types.ts (1)
  • CalendarContext (72-74)
src/modules/calendar/read.ts (2)
src/modules/calendar/types.ts (5)
  • GetCalendarOptions (80-82)
  • CalendarResult (87-96)
  • Attendee (109-116)
  • GetEventOptions (101-104)
  • EventResult (130-164)
src/modules/types.ts (1)
  • CalendarContext (72-74)
src/modules/calendar/create.ts (3)
src/modules/calendar/index.ts (7)
  • Attendee (24-24)
  • createEvent (50-50)
  • CreateEventOptions (29-29)
  • EventResult (21-21)
  • resolveContacts (62-62)
  • quickAdd (50-50)
  • QuickAddOptions (34-34)
src/modules/calendar/types.ts (4)
  • Attendee (109-116)
  • CreateEventOptions (203-219)
  • EventResult (130-164)
  • QuickAddOptions (290-293)
src/modules/calendar/contacts.ts (1)
  • resolveContacts (133-191)
src/modules/calendar/contacts.ts (1)
src/modules/calendar/types.ts (2)
  • ContactEntry (335-340)
  • ResolvedContact (326-330)
src/modules/types.ts (1)
src/modules/index.ts (1)
  • BaseContext (54-54)
src/modules/calendar/update.ts (2)
src/modules/calendar/types.ts (3)
  • Attendee (109-116)
  • UpdateEventOptions (263-268)
  • EventResult (130-164)
src/modules/calendar/contacts.ts (1)
  • resolveContacts (133-191)
src/modules/calendar/__tests__/list.test.ts (1)
src/modules/calendar/list.ts (2)
  • listCalendars (31-110)
  • listEvents (132-251)
src/modules/calendar/__tests__/contacts.test.ts (1)
src/modules/calendar/contacts.ts (2)
  • resolveContacts (133-191)
  • parseContactsFile (36-66)
src/modules/calendar/__tests__/read.test.ts (1)
src/modules/calendar/read.ts (2)
  • getCalendar (32-83)
  • getEvent (142-287)
index.ts (2)
src/modules/calendar/index.ts (9)
  • ListCalendarsOptions (11-11)
  • GetCalendarOptions (18-18)
  • ListEventsOptions (14-14)
  • GetEventOptions (20-20)
  • CreateEventOptions (29-29)
  • UpdateEventOptions (31-31)
  • DeleteEventOptions (32-32)
  • QuickAddOptions (34-34)
  • FreeBusyOptions (36-36)
src/modules/calendar/types.ts (9)
  • ListCalendarsOptions (11-16)
  • GetCalendarOptions (80-82)
  • ListEventsOptions (41-51)
  • GetEventOptions (101-104)
  • CreateEventOptions (203-219)
  • UpdateEventOptions (263-268)
  • DeleteEventOptions (273-277)
  • QuickAddOptions (290-293)
  • FreeBusyOptions (298-303)
🪛 GitHub Actions: CI
src/modules/calendar/contacts.ts

[error] 46-46: ESLint: Expected { after 'if' condition. (curly)

🪛 GitHub Check: Test and Code Quality (22)
src/modules/calendar/contacts.ts

[failure] 46-46:
Expected { after 'if' condition

🪛 LanguageTool
specs/google-calendar-integration.md

[grammar] ~188-~188: Ensure spelling is correct
Context: ...mance Constraints - Cached reads: < 500ms (listEvents, getEvent, getCalendar) - *...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🪛 markdownlint-cli2 (0.18.1)
specs/google-calendar-integration.md

74-74: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


538-538: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


672-672: Bare URL used

(MD034, no-bare-urls)


1212-1212: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


1228-1228: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


1382-1382: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

CHANGELOG.md

32-32: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Security Summary
  • GitHub Check: Load Testing
🔇 Additional comments (15)
src/modules/types.ts (2)

69-74: CalendarContext addition looks consistent with existing module contexts.


5-5: The calendar_v3 import is valid. The googleapis package v144.0.0 exports calendar_v3 types for TypeScript use. No action needed.

Dockerfile (1)

34-34: Comment-only change; helps clarify secret scanning false positives.

package.json (1)

3-4: Version/description update matches the Calendar integration release.

src/modules/calendar/delete.ts (1)

65-72: Cache key patterns between read and delete operations are consistent. The getEvent key in read.ts (line 149) is calendar:getEvent:${eventId} without calendarId, which matches the invalidation pattern in delete.ts (line 67). The listEvents wildcard invalidation pattern correctly handles the complex cache key structure from list.ts. No action needed.

Likely an incorrect or invalid review comment.

src/tools/listTools.ts (1)

247-302: LGTM! Calendar operations well-structured and documented.

The calendar module integration follows the established pattern and includes comprehensive operation definitions with clear signatures and helpful examples. The nine operations cover the full calendar API surface area effectively.

src/modules/calendar/freebusy.ts (1)

50-65: Good: Proper handling of exactOptionalPropertyTypes.

The conditional addition of timeZone (lines 59-61) correctly complies with TypeScript's exactOptionalPropertyTypes configuration, avoiding the assignment of undefined to optional properties.

src/modules/calendar/__tests__/read.test.ts (2)

1-144: Excellent test coverage for getCalendar operation.

The test suite thoroughly validates:

  • Basic calendar retrieval with all fields
  • Cache hit behavior
  • Cache population after API calls
  • Performance monitoring integration
  • Structured logging

The tests properly mock dependencies and use clear arrange-act-assert patterns.


146-432: Comprehensive test coverage for getEvent operation.

The test suite validates critical functionality:

  • Default and custom calendarId handling
  • Complex attendee parsing (organizer, self, optional flags)
  • Recurring events with RRULE
  • Conference data integration
  • Complete caching behavior
  • Performance and logging integration

The tests exercise edge cases effectively and follow Jest best practices.

src/modules/calendar/__tests__/contacts.test.ts (2)

1-240: Excellent TDD implementation for contact resolution.

The test suite comprehensively covers the PAI contact resolution feature:

  • ✅ First name to email resolution
  • ✅ Case-insensitive matching (lines 76-90)
  • ✅ Raw email passthrough
  • ✅ Mixed inputs (names + emails)
  • ✅ Error handling for unknown contacts with helpful error messages
  • ✅ Graceful fallback when contacts file is missing
  • ✅ Environment variable configuration support

The tests properly mock the filesystem and validate both happy paths and error conditions. Great attention to user experience in error messages (listing available contacts).


242-337: Thorough unit tests for parseContactsFile.

The test suite validates:

  • Standard contact format parsing with roles
  • Contacts without optional roles
  • Invalid line filtering
  • Duplicate name handling
  • Empty and whitespace-only content

The tests ensure robust parsing that handles real-world edge cases gracefully.

src/modules/calendar/create.ts (2)

147-418: LGTM! Well-structured event creation with proper validation and caching.

The createEvent implementation is solid:

  • Validates event times before API calls
  • Resolves PAI contact names via resolveContacts
  • Auto-generates Google Meet requestId when needed
  • Properly handles optional fields with exactOptionalPropertyTypes compliance
  • Invalidates relevant caches after creation
  • Tracks performance and logs operation details

451-598: LGTM! QuickAdd implementation is clean and follows the same patterns.

The quickAdd function correctly implements natural language event creation with proper cache invalidation and performance tracking. The documentation clearly notes the limitations compared to createEvent.

src/modules/calendar/update.ts (1)

146-405: LGTM! PATCH-based update implementation is well-structured.

The updateEvent function correctly:

  • Performs partial updates (only modified fields sent to API)
  • Resolves PAI contact names for attendee updates
  • Handles conference data with auto-generated requestId
  • Respects exactOptionalPropertyTypes by conditionally adding fields
  • Invalidates specific event and list caches
  • Supports sendUpdates parameter for attendee notifications
  • Tracks performance and logs operation details
src/modules/calendar/types.ts (1)

1-340: LGTM! Comprehensive and well-organized type definitions.

The type definitions are excellent:

  • Clear separation between options (inputs) and results (outputs)
  • Proper use of optional fields with ? operator
  • References to Google Calendar API types where appropriate (calendar_v3.Schema$ConferenceData)
  • Comprehensive coverage of all calendar operations
  • Follows established patterns from other modules (Gmail, Drive)
  • Well-documented with JSDoc comments

The type system will provide strong compile-time guarantees for the calendar module implementation.

Comment on lines 15 to 20
/**
* Default PAI contacts file path
* Can be overridden via PAI_CONTACTS_PATH environment variable
*/
const DEFAULT_CONTACTS_PATH = '/Users/ossieirondi/PAI/.claude/skills/CORE/USER/CONTACTS.md';

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Remove hardcoded absolute contacts path; add safe default + consider memoizing loads.
DEFAULT_CONTACTS_PATH (Line 19) is user-specific and will break outside that machine; it’s also a privacy/ops footgun. Additionally, loadContacts() re-reads on every resolveContacts() call (Line 137), which is avoidable.

Suggested direction (safe default)
-const DEFAULT_CONTACTS_PATH = '/Users/ossieirondi/PAI/.claude/skills/CORE/USER/CONTACTS.md';
+// Intentionally no user-specific default path:
+// - set PAI_CONTACTS_PATH explicitly when you want name resolution
+// - otherwise resolution runs in "raw emails only" mode
+const DEFAULT_CONTACTS_PATH: string | null = null;
-  const contactsPath = process.env.PAI_CONTACTS_PATH || DEFAULT_CONTACTS_PATH;
+  const contactsPath = process.env.PAI_CONTACTS_PATH || DEFAULT_CONTACTS_PATH;
+  if (!contactsPath) {
+    logger.warn('PAI_CONTACTS_PATH not set; treating all inputs as raw emails');
+    return new Map();
+  }

Also applies to: 74-116, 133-191

🤖 Prompt for AI Agents
In @src/modules/calendar/contacts.ts around lines 15 - 20, DEFAULT_CONTACTS_PATH
is a hardcoded user-specific absolute path and should be replaced with a safe,
non-user-specific default (e.g., relative "./contacts.md") while honoring
process.env.PAI_CONTACTS_PATH if set; update the code that references
DEFAULT_CONTACTS_PATH and ensure missing files are handled gracefully (no
leak/throw). Also memoize loadContacts() by adding a module-level cache (e.g.,
cachedContacts) so subsequent resolveContacts() calls reuse the parsed contacts
instead of re-reading the file each time; invalidate or refresh the cache only
when PAI_CONTACTS_PATH changes or provide an explicit reload function.

Comment on lines +61 to +102
function parseAttendees(attendees: calendar_v3.Schema$EventAttendee[] | undefined): Attendee[] | undefined {
if (!attendees || attendees.length === 0) {
return undefined;
}

return attendees.map((attendee) => {
const parsed: Attendee = {
email: attendee.email ?? '',
};

// Use intermediate variables to help TypeScript narrow types
const displayName = attendee.displayName;
if (typeof displayName === 'string') {
parsed.displayName = displayName;
}

const responseStatus = attendee.responseStatus;
if (responseStatus === 'needsAction' || responseStatus === 'declined' || responseStatus === 'tentative' || responseStatus === 'accepted') {
parsed.responseStatus = responseStatus;
}

if (attendee.organizer === true) {
parsed.organizer = true;
} else if (attendee.organizer === false) {
parsed.organizer = false;
}

if (attendee.self === true) {
parsed.self = true;
} else if (attendee.self === false) {
parsed.self = false;
}

if (attendee.optional === true) {
parsed.optional = true;
} else if (attendee.optional === false) {
parsed.optional = false;
}

return parsed;
});
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Extract duplicated parsing logic to a shared utility.

The parseAttendees function is duplicated between create.ts and update.ts (identical implementation at lines 60-101 in update.ts). Extract this to the shared utility module alongside validateEventTimes.

♻️ Suggested refactor

Add to src/modules/calendar/utils.ts:

import type { calendar_v3 } from 'googleapis';
import type { Attendee } from './types.js';

/**
 * Parse attendees from Google Calendar API response
 */
export function parseAttendees(
  attendees: calendar_v3.Schema$EventAttendee[] | undefined
): Attendee[] | undefined {
  if (!attendees || attendees.length === 0) {
    return undefined;
  }

  return attendees.map((attendee) => {
    const parsed: Attendee = {
      email: attendee.email ?? '',
    };

    const displayName = attendee.displayName;
    if (typeof displayName === 'string') {
      parsed.displayName = displayName;
    }

    const responseStatus = attendee.responseStatus;
    if (responseStatus === 'needsAction' || responseStatus === 'declined' || responseStatus === 'tentative' || responseStatus === 'accepted') {
      parsed.responseStatus = responseStatus;
    }

    if (attendee.organizer === true) {
      parsed.organizer = true;
    } else if (attendee.organizer === false) {
      parsed.organizer = false;
    }

    if (attendee.self === true) {
      parsed.self = true;
    } else if (attendee.self === false) {
      parsed.self = false;
    }

    if (attendee.optional === true) {
      parsed.optional = true;
    } else if (attendee.optional === false) {
      parsed.optional = false;
    }

    return parsed;
  });
}

Then import in both files:

+import { parseAttendees } from './utils.js';
-function parseAttendees(...) { ... }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In @src/modules/calendar/create.ts around lines 61 - 102, The parseAttendees
implementation is duplicated in create.ts and update.ts; move it into the shared
calendar utils alongside validateEventTimes by creating a single exported
function parseAttendees(attendees: calendar_v3.Schema$EventAttendee[] |
undefined): Attendee[] | undefined in src/modules/calendar/utils.ts, importing
necessary types (calendar_v3 and Attendee) in that file, then replace the
duplicate implementations in create.ts and update.ts with an import of the
shared parseAttendees and call it; ensure function name and signature stay
identical so callers need only the import change.

Comment on lines +45 to +85
export async function deleteEvent(
options: DeleteEventOptions,
context: CalendarContext
): Promise<{ success: boolean; message: string }> {
const {
calendarId = 'primary',
eventId,
sendUpdates = 'none',
} = options;

// Build params
const params: calendar_v3.Params$Resource$Events$Delete = {
calendarId,
eventId,
sendUpdates,
};

// Execute delete - Google Calendar API returns 204 No Content on success
await context.calendar.events.delete(params);

// Invalidate caches for this event and list caches
const cacheKeys = [
`calendar:getEvent:${eventId}`,
`calendar:listEvents:${calendarId}:*`,
];
for (const pattern of cacheKeys) {
await context.cacheManager.invalidate(pattern);
}

context.performanceMonitor.track('calendar:deleteEvent', Date.now() - context.startTime);
context.logger.info('Deleted calendar event', {
calendarId,
eventId,
sendUpdates,
});

return {
success: true,
message: 'Event deleted successfully',
};
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add input validation + error handling (and make cache invalidation best-effort).

Right now, a missing eventId and/or a Redis/cache outage can produce confusing failures (or fail the call after the event is already deleted remotely).

Proposed fix
 export async function deleteEvent(
   options: DeleteEventOptions,
   context: CalendarContext
 ): Promise<{ success: boolean; message: string }> {
+  const opStart = Date.now();
   const {
     calendarId = 'primary',
     eventId,
     sendUpdates = 'none',
   } = options;
 
+  if (!eventId) {
+    throw new Error('deleteEvent: "eventId" is required');
+  }
+
   // Build params
   const params: calendar_v3.Params$Resource$Events$Delete = {
     calendarId,
     eventId,
     sendUpdates,
   };
 
-  // Execute delete - Google Calendar API returns 204 No Content on success
-  await context.calendar.events.delete(params);
-
-  // Invalidate caches for this event and list caches
-  const cacheKeys = [
-    `calendar:getEvent:${eventId}`,
-    `calendar:listEvents:${calendarId}:*`,
-  ];
-  for (const pattern of cacheKeys) {
-    await context.cacheManager.invalidate(pattern);
-  }
-
-  context.performanceMonitor.track('calendar:deleteEvent', Date.now() - context.startTime);
-  context.logger.info('Deleted calendar event', {
-    calendarId,
-    eventId,
-    sendUpdates,
-  });
+  try {
+    // Execute delete - Google Calendar API returns 204 No Content on success
+    await context.calendar.events.delete(params);
+
+    // Invalidate caches for this event and list caches (best-effort)
+    const cacheKeys = [
+      `calendar:getEvent:${eventId}`,
+      `calendar:listEvents:${calendarId}:*`,
+    ];
+    for (const pattern of cacheKeys) {
+      try {
+        await context.cacheManager.invalidate(pattern);
+      } catch (e) {
+        context.logger.warn('Cache invalidation failed after deleteEvent', {
+          pattern,
+          error: e instanceof Error ? e.message : String(e),
+        });
+      }
+    }
+
+    context.logger.info('Deleted calendar event', {
+      calendarId,
+      eventId,
+      sendUpdates,
+    });
+    context.performanceMonitor.track('calendar:deleteEvent', Date.now() - opStart);
+  } catch (e) {
+    context.performanceMonitor.track('calendar:deleteEvent', Date.now() - opStart, true);
+    context.logger.error('Failed to delete calendar event', {
+      calendarId,
+      eventId,
+      sendUpdates,
+      error: e instanceof Error ? e.message : String(e),
+    });
+    throw e;
+  }
 
   return {
     success: true,
     message: 'Event deleted successfully',
   };
 }

Comment on lines +39 to +48
// Check cache first (short TTL for time-sensitive data)
const cacheKey = `calendar:checkFreeBusy:${JSON.stringify(options)}`;
const cached = await context.cacheManager.get(cacheKey);
if (cached) {
context.performanceMonitor.track(
'calendar:checkFreeBusy',
Date.now() - context.startTime
);
return cached as FreeBusyResult;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Cache key serialization may cause unnecessary cache misses.

Using JSON.stringify(options) for the cache key can result in cache misses when the same query is made with properties in different orders. JavaScript object property order isn't guaranteed, so {timeMin: "...", timeMax: "..."} and {timeMax: "...", timeMin: "..."} would produce different cache keys for identical queries.

🔧 Recommended fix for stable cache keys
- const cacheKey = `calendar:checkFreeBusy:${JSON.stringify(options)}`;
+ // Create stable cache key by sorting properties
+ const cacheKey = `calendar:checkFreeBusy:${timeMin}:${timeMax}:${items.map(i => i.id).sort().join(',')}${timeZone ? `:${timeZone}` : ''}`;

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In @src/modules/calendar/freebusy.ts around lines 39 - 48, The cache key uses
JSON.stringify(options) which can produce different strings for the same logical
query when object property order differs; change the key generation in the
calendar:checkFreeBusy path (where cacheKey is defined and
context.cacheManager.get is called) to use a stable/canonical serialization of
options (e.g., sort object keys before stringifying or build the key from
explicit known fields such as timeMin, timeMax, calendarId, attendees) so
identical queries always produce the same cacheKey; keep the rest of the flow
(context.performanceMonitor.track and returning cached as FreeBusyResult)
unchanged.

Comment on lines +97 to +100
// Cache the result (Note: current CacheManager doesn't support per-operation TTL)
// TODO: Implement 60s TTL for freeBusy when CacheManager supports configurable TTL
// Per spec: "cache with short TTL (60s), highly time-sensitive"
await context.cacheManager.set(cacheKey, result);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, locate and inspect the freebusy.ts file
find . -name "freebusy.ts" -type f

Repository: AojdevStudio/gdrive

Length of output: 97


🏁 Script executed:

# Search for CacheManager implementation
find . -name "*cache*" -type f | grep -E "\.(ts|js)$" | head -20

Repository: AojdevStudio/gdrive

Length of output: 45


🏁 Script executed:

# Look for any specification or documentation about free/busy TTL
rg -i "free.*busy|freebusy" --type ts --type tsx -A 5 -B 5 | head -100

Repository: AojdevStudio/gdrive

Length of output: 90


🏁 Script executed:

# Search for TTL requirements in comments or specs
rg -i "ttl|time.*sensitive" --type ts --type tsx | head -50

Repository: AojdevStudio/gdrive

Length of output: 90


🏁 Script executed:

# Read the freebusy.ts file
cat -n ./src/modules/calendar/freebusy.ts

Repository: AojdevStudio/gdrive

Length of output: 4345


🏁 Script executed:

# Find CacheManager implementation
find . -type f -name "*.ts" -o -name "*.js" | xargs grep -l "class CacheManager\|interface CacheManager" 2>/dev/null | head -5

Repository: AojdevStudio/gdrive

Length of output: 201


🏁 Script executed:

# Search for TTL references in the codebase
rg "TTL|ttl" --type ts -B 2 -A 2

Repository: AojdevStudio/gdrive

Length of output: 3304


🏁 Script executed:

# Look for any Google Calendar API specs or free/busy documentation
rg "free.*busy|freebusy" -B 3 -A 3

Repository: AojdevStudio/gdrive

Length of output: 13896


🏁 Script executed:

# Find the CacheManager file
find . -type f -name "*cache*" | grep -v node_modules | grep -v test

Repository: AojdevStudio/gdrive

Length of output: 45


🏁 Script executed:

# Read CacheManager implementation to see the set() method signature
rg "class.*CacheManager|interface.*CacheManager" -A 30 --type ts

Repository: AojdevStudio/gdrive

Length of output: 8962


🏁 Script executed:

# Check the set method specifically
rg "set\(" -A 10 --type ts | grep -A 10 "cacheManager\|CacheManager" | head -50

Repository: AojdevStudio/gdrive

Length of output: 2783


🏁 Script executed:

# Check the CHANGELOG more carefully for the 60s claim
cat CHANGELOG.md | grep -A 5 -B 5 "60s"

Repository: AojdevStudio/gdrive

Length of output: 391


🏁 Script executed:

# Search for any Google Calendar specs or documentation about free/busy timing requirements
rg "60s|60.*second|time.*sensitive|highly.*sensitive" --type ts -B 3 -A 3

Repository: AojdevStudio/gdrive

Length of output: 2423


🏁 Script executed:

# Look at the test file to see if TTL behavior is tested
cat -n src/modules/calendar/__tests__/freebusy.test.ts | grep -A 10 -B 10 "TTL\|60\|cache"

Repository: AojdevStudio/gdrive

Length of output: 4705


🏁 Script executed:

# Check if there's any specification file mentioning free/busy requirements
find . -name "*.md" -type f | xargs grep -l "free.*busy\|freebusy" | head -5

Repository: AojdevStudio/gdrive

Length of output: 116


Implement per-operation TTL support in CacheManager for time-sensitive free/busy data.

Free/busy data requires a 60-second TTL per spec ("cache with short TTL (60s), highly time-sensitive"), but the current implementation uses the default 5-minute TTL due to CacheManager.set() not accepting configurable TTL parameters. This mismatch could result in stale availability data and cause scheduling conflicts.

Priority: Enhance CacheManager to support per-operation TTL configuration, allowing free/busy calls to specify shorter expiration times. This is currently blocked as noted in the TODO comments at lines 97-99.

Comment on lines +146 to +154
const { calendarId = 'primary', eventId } = options;

// Check cache first
const cacheKey = `calendar:getEvent:${eventId}`;
const cached = await context.cacheManager.get(cacheKey);
if (cached) {
context.performanceMonitor.track('calendar:getEvent', Date.now() - context.startTime);
return cached as EventResult;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix getEvent cache key to include calendarId (avoid cross-calendar collisions).
Right now the cache key is only eventId (Line 149). If two calendars have the same eventId, cached results can bleed across.

Proposed fix
-  const cacheKey = `calendar:getEvent:${eventId}`;
+  const cacheKey = `calendar:getEvent:${calendarId}:${eventId}`;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const { calendarId = 'primary', eventId } = options;
// Check cache first
const cacheKey = `calendar:getEvent:${eventId}`;
const cached = await context.cacheManager.get(cacheKey);
if (cached) {
context.performanceMonitor.track('calendar:getEvent', Date.now() - context.startTime);
return cached as EventResult;
}
const { calendarId = 'primary', eventId } = options;
// Check cache first
const cacheKey = `calendar:getEvent:${calendarId}:${eventId}`;
const cached = await context.cacheManager.get(cacheKey);
if (cached) {
context.performanceMonitor.track('calendar:getEvent', Date.now() - context.startTime);
return cached as EventResult;
}
🤖 Prompt for AI Agents
In @src/modules/calendar/read.ts around lines 146 - 154, getEvent's cache key
currently only uses eventId which can collide across calendars; update the
cacheKey construction in getEvent to include calendarId (e.g.,
`calendar:getEvent:${calendarId}:${eventId}`) and ensure any corresponding cache
writes use the same composite key when calling context.cacheManager.set, so that
context.cacheManager.get/cache misses are consistent and results don't bleed
between calendars; keep references to cacheKey, calendarId, eventId, getEvent,
context.cacheManager.get and context.cacheManager.set.

Comment on lines +157 to +160
// Validate event times if both start and end are being updated
if (updates.start && updates.end) {
validateEventTimes(updates.start, updates.end);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Consider validating partial time updates.

The validation only runs when both start and end are updated together. If a user updates only start or only end, the validation is skipped, potentially creating an invalid event where the end time is before the start time.

While the Google Calendar API will likely reject such updates, catching this earlier with a clear error message would improve user experience.

💡 Potential enhancement

Consider fetching the existing event when only one time field is updated to validate the combination:

// Validate event times if both start and end are being updated
if (updates.start && updates.end) {
  validateEventTimes(updates.start, updates.end);
} else if (updates.start || updates.end) {
  // Fetch existing event to validate partial update
  const existing = await context.calendar.events.get({
    calendarId,
    eventId,
  });
  
  const finalStart = updates.start || existing.data.start;
  const finalEnd = updates.end || existing.data.end;
  
  if (finalStart && finalEnd) {
    validateEventTimes(finalStart, finalEnd);
  }
}

This adds an extra API call but ensures time consistency. Alternatively, accept the current behavior and rely on the API to reject invalid updates.

🤖 Prompt for AI Agents
In @src/modules/calendar/update.ts around lines 157 - 160, The current update
logic only calls validateEventTimes when both updates.start and updates.end are
present, which misses partial updates; modify the update handler around
validateEventTimes to, when only one of updates.start or updates.end is
provided, fetch the existing event via context.calendar.events.get using
calendarId and eventId, derive finalStart = updates.start || existing.data.start
and finalEnd = updates.end || existing.data.end, and call
validateEventTimes(finalStart, finalEnd) if both are present to ensure start ≤
end before sending the patch.

Fixes:
1. ESLint curly violation in contacts.ts:47 - added braces around continue
2. Removed hardcoded user path in contacts.ts - DEFAULT_CONTACTS_PATH now null
3. Extracted validateEventTimes to shared utils.ts (DRY principle)
4. Added 'text' language to CHANGELOG.md code block (MD040)

Test updates:
- contacts.test.ts now sets PAI_CONTACTS_PATH for tests needing contact resolution
- Added test for "no contacts path" fallback behavior

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@github-actions
Copy link

github-actions bot commented Jan 9, 2026

📊 Type Coverage Report

Type Coverage: 97.67%

This PR's TypeScript type coverage analysis is complete.
Check the full report in the workflow artifacts.

AojdevStudio and others added 2 commits January 8, 2026 18:46
Documents the optional environment variable for Calendar module
contact resolution feature.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- @modelcontextprotocol/sdk: Updated to fix ReDoS vulnerability
- qs: Updated to fix DoS via memory exhaustion

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@github-actions
Copy link

github-actions bot commented Jan 9, 2026

📊 Type Coverage Report

Type Coverage: 97.67%

This PR's TypeScript type coverage analysis is complete.
Check the full report in the workflow artifacts.

1 similar comment
@github-actions
Copy link

github-actions bot commented Jan 9, 2026

📊 Type Coverage Report

Type Coverage: 97.67%

This PR's TypeScript type coverage analysis is complete.
Check the full report in the workflow artifacts.

@AojdevStudio AojdevStudio merged commit cd1234b into main Jan 9, 2026
24 of 25 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant