| name | apple-calendar-scheduler |
| description | Parse natural-language schedule requests into Apple Calendar events, especially Chinese phrases such as "明早 8 点开会" or multiple events in one sentence. Use when Codex needs to extract event details, check Apple Calendar conflicts before writing, warn about overlaps, and create events with a default 30-minute reminder on macOS. |
Apple Calendar Scheduler
Convert natural-language schedule requests into structured events, check Apple Calendar for overlaps, and create events only after the event data is complete enough to write safely.
Workflow
Follow this sequence every time:
- Parse the user's request into one or more event objects using the schema in references/event-schema.md.
- Normalize relative time phrases into absolute ISO datetimes in the user's local timezone.
- Fill
alert_minutes_before with 30 when the user did not specify a reminder.
- Infer reasonable defaults for missing fields (see Operating Rules). Only ask when truly ambiguous.
- Check for overlaps before creating anything:
- Use scripts/detect_conflicts.py for one or more candidate events.
- Treat any time overlap as a conflict unless the user explicitly says to allow it.
- If conflicts exist, stop before writing and tell the user which existing events overlap.
- If there are no conflicts, create the events using the JXA method (see "JXA Event Creation" section). Python scripts are fallback only if JXA fails..
- Return a compact summary of what was created, including time range, calendar name, and reminder lead time.
Operating Rules
- Default to a 30-minute reminder.
- Do NOT ask the user for confirmation or clarification. Infer reasonable defaults and create events directly.
- Missing start time for a morning event with a deadline constraint → start 1 hour before the deadline.
- Missing duration for meals / social gatherings → default 2 hours.
- Missing duration for meetings → default 1 hour.
- Missing calendar → use the script default or "个人" calendar.
- Only stop and ask when the user's intent is genuinely ambiguous (e.g., conflicting instructions or multiple equally valid interpretations).
- Do not silently shift an event to avoid a conflict. Surface the conflict first.
- When the user gives multiple events in one sentence, split them into separate event objects and validate each one.
- Prefer the user's explicitly named calendar. If none is given, let the script choose its default target calendar.
- If Calendar access is denied, stop and tell the user to grant automation/calendar permission, then retry.
Commands
List existing events in a window:
python3 scripts/list_events.py --start 2026-03-24T00:00:00+08:00 --end 2026-03-25T00:00:00+08:00
Check one or more candidate events for conflicts:
python3 scripts/detect_conflicts.py --input-file /tmp/events.json
Create events after conflict checks pass:
python3 scripts/create_event.py --input-file /tmp/events.json
Parse Chinese schedule text into structured events and unresolved questions:
python3 scripts/parse_schedule.py --text "明早1个小时买菜,9:30之后不行,然后晚上6点朋友到家里聚餐4小时"
Suggest concrete slots for unresolved items:
python3 scripts/suggest_slots.py --input-file /tmp/parsed.json
Run the full workflow in one shot:
python3 scripts/schedule_from_text.py --text "明早1个小时买菜,9:30之后不行,然后晚上6点朋友到家里聚餐4小时"
Event Extraction Guidance
Map user language into concise event titles:
- "明早买菜 1 小时" ->
title: 买菜
- "晚上 6 点朋友到家里聚餐" ->
title: 朋友聚餐, location: 家里
Infer an end time only when duration is explicit or strongly implied:
- "明天 8:30 开会 1 小时" -> end at
09:30
- "晚上 6 点聚餐" -> default 2 hours (18:00-20:00) unless user says otherwise
Convert constraints like "9:30 之后不行" into a scheduling constraint: infer the latest possible start time and schedule accordingly. Do not ask for confirmation.
Use the parser output conservatively:
- If
events is non-empty and unresolved is empty, move on to conflict detection.
- If a segment lands in .unresolved., apply reasonable defaults first. Only ask as a last resort, or use scripts/suggest_slots.py to propose concrete windows.
- When
constraints are present without an exact start, treat them as availability hints, not a scheduled event.
Resources
JXA Event Creation (Primary Method)
Python scripts require Python 3.10+ due to type union syntax. On macOS with Python 3.9, use JXA directly via osascript -l JavaScript.
How to Create Events
IMPORTANT: Use cal.events.push(newEvent) — do NOT use new app.Event({..., calendar: cal}) as it silently fails.
osascript -l JavaScript <<'JXA'
const app = Application("Calendar");
app.includeStandardAdditions = true;
const cal = app.calendars.byName("个人");
const s1 = new Date("2026-03-24T08:30:00+08:00");
const e1 = new Date("2026-03-24T09:30:00+08:00");
const ev1 = new app.Event({summary: "买菜", startDate: s1, endDate: e1});
cal.events.push(ev1);
const s2 = new Date("2026-03-24T18:00:00+08:00");
const e2 = new Date("2026-03-24T20:00:00+08:00");
const ev2 = new app.Event({summary: "朋友聚餐", startDate: s2, endDate: e2});
cal.events.push(ev2);
JSON.stringify({ok: true, created: 2});
JXA
How to Delete Events
osascript -l JavaScript <<'JXA'
const app = Application("Calendar");
const cal = app.calendars.byName("个人");
const events = cal.events();
events.filter(e => e.summary() === "买菜").forEach(e => e.delete());
JSON.stringify({ok: true});
JXA
Notes
new Date("2026-03-24T08:30:00+08:00") works correctly in JXA; toISOString() shows UTC but Calendar displays in local time.
- Always verify with
list_events.py after creation.
- When the Python scripts fail with
TypeError: unsupported operand type(s) for |, fall back to JXA directly.