skills/meetings/SKILL.md
Coordinate meetings using calendar and email integration. Find mutual availability, send invites, create calendar events, and manage meeting agendas with prep documents.
npx skillsauth add ticruz38/skills meetingsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Coordinate meetings seamlessly using calendar and email integration. Schedule meetings, find mutual availability, manage agendas, and send invites all from your agent.
npm install
npm run build
The meetings skill requires both calendar and email to be connected:
# Connect Google account with Calendar and Gmail scopes
node ../google-oauth/dist/cli.js connect default calendar email
# Check connection status
node dist/cli.js status
node dist/cli.js health
# Basic meeting
node dist/cli.js schedule --title "Team Sync" --attendees "[email protected],[email protected]"
# With all options
node dist/cli.js schedule \
--title "Project Review" \
--attendees "[email protected],[email protected],[email protected]" \
--duration 90 \
--description "Quarterly project review" \
--location "Conference Room A" \
--days 14 \
--prep
# With agenda items
node dist/cli.js schedule \
--title "Sprint Planning" \
--attendees "[email protected]" \
--duration 120 \
--agenda-0 "Review previous sprint" --agenda-0-duration 20 \
--agenda-1 "Story estimation" --agenda-1-duration 60 \
--agenda-2 "Sprint commitment" --agenda-2-duration 20 \
--prep
# Quick 30-minute meeting (minimal options)
node dist/cli.js quick --title "Quick Sync" --attendees "[email protected]"
# With duration
node dist/cli.js quick --title "Code Review" --attendees "[email protected]" --duration 60
# List available templates
node dist/cli.js templates
# Schedule from template
node dist/cli.js template "Standup" --attendees "[email protected]"
# With custom title
node dist/cli.js template "1-on-1" --title "1-on-1 with Alice" --attendees "[email protected]"
# List upcoming meetings
node dist/cli.js list
# List more meetings
node dist/cli.js list --days 30 --limit 50
# Filter by status
node dist/cli.js list --status scheduled
node dist/cli.js get meeting_abc123
# Cancel and notify attendees
node dist/cli.js cancel meeting_abc123
# Cancel without notifications
node dist/cli.js cancel meeting_abc123 --no-notify
node dist/cli.js delete meeting_abc123
# Find 60-minute slots in next 7 days
node dist/cli.js free
# Find 30-minute slots in next 3 days
node dist/cli.js free --duration 30 --days 3
# Re-send invites for a meeting
node dist/cli.js invite meeting_abc123
# Send agenda and prep materials
node dist/cli.js prep meeting_abc123
import { MeetingsSkill } from '@openclaw/meetings';
// Create skill for default profile
const meetings = new MeetingsSkill();
// Or for specific profile
const workMeetings = MeetingsSkill.forProfile('work');
const status = await meetings.getStatus();
console.log('Connected:', status.connected);
console.log('Calendar:', status.calendarConnected);
console.log('Email:', status.emailConnected);
const result = await meetings.schedule({
title: 'Team Standup',
description: 'Daily team sync',
duration: 30,
attendees: ['[email protected]', '[email protected]'],
timeMin: new Date().toISOString(),
timeMax: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString(),
location: 'Conference Room A',
agenda: [
{ title: 'What did you do yesterday?', duration: 10 },
{ title: 'What will you do today?', duration: 10 },
{ title: 'Any blockers?', duration: 10 },
],
sendInvites: true,
sendPrepEmail: true,
});
if (result.success) {
console.log('Meeting scheduled:', result.meeting?.id);
console.log('Time:', result.meeting?.startTime);
} else {
console.error('Failed:', result.error);
}
const result = await meetings.scheduleFromTemplate('Standup', {
title: 'Daily Standup',
attendees: ['[email protected]'],
timeMin: new Date().toISOString(),
timeMax: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString(),
sendInvites: true,
});
const availability = await meetings.findAvailability({
attendees: ['[email protected]', '[email protected]'],
duration: 60,
timeMin: new Date().toISOString(),
timeMax: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString(),
});
for (const slot of availability.availableSlots) {
console.log(`Available: ${slot.start} - ${slot.end}`);
}
const meetingList = await meetings.listMeetings({
status: 'scheduled',
from: new Date().toISOString(),
to: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString(),
limit: 50,
});
for (const meeting of meetingList) {
console.log(`${meeting.title}: ${meeting.startTime}`);
}
const meeting = await meetings.getMeeting('meeting_abc123');
console.log('Title:', meeting?.title);
console.log('Attendees:', meeting?.attendees.map(a => a.email));
console.log('Agenda:', meeting?.agenda.map(item => item.title));
const updated = await meetings.updateMeeting('meeting_abc123', {
title: 'Updated Title',
startTime: new Date(Date.now() + 2 * 60 * 60 * 1000).toISOString(),
endTime: new Date(Date.now() + 3 * 60 * 60 * 1000).toISOString(),
});
// Cancel and notify attendees
await meetings.cancelMeeting('meeting_abc123', true);
// Cancel without notifications
await meetings.cancelMeeting('meeting_abc123', false);
const meeting = await meetings.getMeeting('meeting_abc123');
if (meeting) {
await meetings.sendMeetingInvite(meeting);
}
const meeting = await meetings.getMeeting('meeting_abc123');
if (meeting) {
await meetings.sendPrepEmail(meeting);
}
The meetings skill includes several pre-defined templates:
const template = await meetings.createTemplate({
name: 'Architecture Review',
title: 'Architecture Review: {topic}',
description: 'Review proposed architecture changes',
duration: 90,
agenda: [
{ id: '1', title: 'Problem statement', duration: 10 },
{ id: '2', title: 'Proposed solution', duration: 30 },
{ id: '3', title: 'Discussion', duration: 40 },
{ id: '4', title: 'Decision', duration: 10 },
],
prepDocuments: [
{ id: '1', name: 'Architecture Doc', type: 'link', required: true },
],
});
const templates = await meetings.listTemplates();
for (const template of templates) {
console.log(`${template.name}: ${template.duration} min`);
}
interface Meeting {
id: string;
title: string;
description?: string;
startTime: string; // ISO datetime
endTime: string; // ISO datetime
timeZone: string;
location?: string;
videoLink?: string;
attendees: MeetingAttendee[];
agenda: AgendaItem[];
prepDocuments: PrepDocument[];
calendarEventId?: string;
status: 'draft' | 'scheduled' | 'cancelled' | 'completed';
notes?: string;
createdAt: string;
updatedAt: string;
}
interface MeetingAttendee {
email: string;
name?: string;
optional?: boolean;
responseStatus?: 'needsAction' | 'declined' | 'tentative' | 'accepted';
}
interface AgendaItem {
id: string;
title: string;
duration: number; // in minutes
presenter?: string;
description?: string;
}
interface PrepDocument {
id: string;
name: string;
url?: string;
content?: string;
type: 'link' | 'file' | 'note';
required: boolean;
}
Meeting data is stored in:
~/.openclaw/skills/meetings/meetings.db
Tables:
meetings - Meeting recordstemplates - Meeting templatesManage meetings for different accounts:
import { MeetingsSkill } from '@openclaw/meetings';
// Work account
const work = MeetingsSkill.forProfile('work');
// Personal account
const personal = MeetingsSkill.forProfile('personal');
// Schedule independently
await work.schedule({ /* work meeting */ });
await personal.schedule({ /* personal meeting */ });
Each profile needs separate authentication:
node ../google-oauth/dist/cli.js connect work calendar email
node ../google-oauth/dist/cli.js connect personal calendar email
try {
const result = await meetings.schedule({
title: 'Team Meeting',
duration: 60,
attendees: ['[email protected]'],
timeMin: new Date().toISOString(),
timeMax: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString(),
});
if (!result.success) {
console.log('Could not schedule:', result.error);
console.log('Alternative slots:', result.suggestedSlots);
}
} catch (error) {
console.error('Error:', error.message);
}
# Type checking
npm run typecheck
# Build
npm run build
# Check status
npm run status
# List templates
npm run cli -- templates
# Schedule a meeting
npm run cli -- quick --title "Test Meeting" --attendees "[email protected]"
# List meetings
npm run cli -- list --days 30
Authenticate with google-oauth first:
node ../google-oauth/dist/cli.js connect default calendar email
Check health of dependencies:
node ../calendar/dist/cli.js health
node ../email/dist/cli.js health
Expand your search window:
node dist/cli.js schedule --title "Meeting" --attendees "[email protected]" --days 14
@openclaw/calendar: Calendar integration@openclaw/email: Email integration@openclaw/google-oauth: Authentication (via calendar/email)@openclaw/auth-provider: Base authenticationsqlite3: Local storagetesting
Suggest recipes based on dietary preferences, available ingredients, and cuisine preferences
development
Extract data from receipt photos using Google Vision API
business
QuickBooks Online integration for accounting sync - sync customers, invoices, and transactions with two-way sync and conflict resolution
testing
QuickBooks OAuth adapter for QuickBooks Online accounting integration. Built on top of auth-provider for secure token management with automatic refresh, multi-profile support, sandbox/production toggle, and health checks.