apple-smart-schedule · 苹果智能日程提醒
⚠️ 平台限制(先读)
本 skill 仅在 macOS 上运行:它通过 osascript/remindctl 调用 Mac 自带的「日历」和「提醒事项」App 来创建日程。
- ✅ 创建结果经 iCloud 自动同步到你登录同一 Apple ID 的 iPhone / iPad。
- ❌ 不能直接在 Windows / Android / Linux 上用,也不能在 iPhone 上直接运行这些脚本。
- 授权(重要):首次运行脚本时,macOS 会弹出「xxx 想要控制「日历」/「提醒事项」」对话框,点「好」即可(只此一次)。每换一个终端 App 或新环境首次用,都会再弹一次——因为授权是按"调用的终端 App"分别记录的(Terminal 授权了不代表 iTerm 也授权了);点过就好,无需去设置里找。只有当弹窗没出现、或曾经点过"不允许"时,才去「系统设置 › 隐私与安全性 › 自动化」把对应终端的 Calendar / Reminders 开关手动打开。
它做什么
用户用大白话说一件事(或发张截图),本 skill:
- 解析出 事件标题、开始时间、结束时间(可选)、地点、事件类型;
- 按事件类型套用 默认提前量(飞机/高铁/开庭/会议/截止/社交 各不同);
- 在苹果日历建 1 个事件(当天已有同类事件则更新它,不重复新建),在提醒事项建 一串提前提醒;
- 回执告诉用户建了什么。
通用场景(同一套逻辑,不限于出行):
| 类型 | 示例输入 | 智能默认提前量 |
|---|---|---|
| ✈️ 航班 | "东航 MU5137 7/20 8:30 浦东起飞" | 前1天9点 + 前3h + 前1h |
| 🚄 高铁 | "G1234 北京南→上海虹桥 8/1 14:00" | 前1h + 前30min |
| ⚖️ 开庭 | "下周三 14:00 开庭" | 前1天9点 + 前3h + 前1h |
| 💼 会议 | "明天上午 10 点开会" | 前1天9点 + 前1h |
| ⏰ 期限 | "15 号上诉期截止" | 前3天 + 前1天 + 当天9点 |
| 🍻 社交 | "周五晚和张总吃饭" | 前1天9点 + 前2h |
| 🎯 其他 | "后天上午去体检" | 前1h |
核心流程(AI 执行步骤)
第 0 步:读配置(每次都要)
读 $SKILL_DIR/config/config.json,拿到:时区、默认日历名 default_calendar、默认提醒清单名 default_reminder_list、各类事件提前量 lead_times。
再读 $SKILL_DIR/references/lead-times.md,掌握事件类型判断规则和提前量计算方法。
第 1 步:解析输入
- 文字输入:直接从用户消息提取 时间(日期+时分)、事件、地点、航班号/车次 等。
- 截图输入(机票确认页、12306、微信邀约等):用视觉能力读出上述字段。
- 时间按
config.timezone(默认 Asia/Shanghai)理解。含糊时(如"下周三"未指定时区、只说"下午"),按最常见理解并在回执里点明你的理解,让用户一眼能纠正。 - 没给结束时间 → 事件默认时长 60 分钟(会议/社交够用;航班/高铁若能推断时长更好)。
第 2 步:判断事件类型
按 references/lead-times.md 的关键词表归类到 flight / train / court / meeting / deadline / social / default 之一。一条消息可能含多个事件(如出差=航班+到达后会议),分别处理。
第 3 步:算出每条提醒的绝对时间
从 config.lead_times[类型] 取数组,按 lead-times.md 的格式规则把每条换算成绝对时间(ISO,Asia/Shanghai)。
用户当次覆盖优先:若用户明说"提前 X 小时""只提醒一次",则当次只用用户指定的,忽略默认。
第 4 步:查重与写入(更新还是新建)
默认先查重,避免用户补充机票/酒店/改签信息时重复新建事件。用户明说「再建一个」「分开记」时才跳过查重直接新建。
# 1) 查事件日期当天的已有事件(默认查 default_calendar;用户提过别的日历就查那个;指称模糊用 ALL)
$SKILL_DIR/scripts/find_event.sh "<日历名|ALL>" "<事件日期YYYY-MM-DD>"
# 2a) 唯一匹配 → 更新已有事件(第 2–6 参数传 "-" = 不改该项;备注是整段替换,补充信息要先合并原备注再写入)
$SKILL_DIR/scripts/update_event.sh "<uid>" ["<新标题>"] ["<新开始ISO>"] ["<新结束ISO>"] ["<新地点>"] ["<新备注>"]
# 定位也可用标题关键词兜底;多匹配会拒绝并列出候选。详见下文「修改已有事件」一节。
# 2b) 无匹配 → 新建
$SKILL_DIR/scripts/create_event.sh "<标题>" "<default_calendar>" "<开始ISO>" ["<结束ISO>"] ["<地点>"] ["<备注>"]
# 3) 提醒:对每个提前量建一条
$SKILL_DIR/scripts/create_reminder.sh "<标题>" "<default_reminder_list>" "<提醒绝对ISO>"
查重匹配规则(对 find 输出的候选逐条判断):
- 同一天,且 标题含相同关键标识(航班号/车次/案号/活动名)或 时间相近(±3 小时)且主题一致 → 同一事件;
- 全天事件(00:00 起、无明确时长)按「当天」参与匹配;
- 唯一匹配 → update;多个候选或拿不准 → 把候选列给用户选,不要猜;
- 0 匹配但用户说「我已经建过了」→ 窗口扩到前后各 1 天重查(
find_event.sh "<日历>" "<前一天>" "<后两天>"); - 走了更新路径就不要再新建提醒(很可能之前已建过,重复建=骚扰),回执里问一句要不要顺带补提醒;
- 更新的如果是用户手动建的事件,回执里说清楚改了哪些字段(时间/地点/备注)。
写入要点:
- 一次出差 = 一个日历事件:同一行程的去程/住宿/返程(及当地多个安排)默认合并进一个事件——起止 = 去程出发 → 返程落地,标题 = 行程名(如
XX大学 · 行业前沿讲座(西安)),航班/酒店细节分层写进备注(✈️ 去程… / 🏨 住宿… / ✈️ 返程…)。不要按交通/住宿拆成多条事件;提醒仍按行程内关键节点(如每个航班前 3h)建。 - 单段行程(只有一个航班/一场会议)标题示例:
✈️ MU5137 上海浦东→北京首都 - 航班/出行录关键字段:参照
references/lead-times.md的「航班详细字段录入模板」,把 航司/舱位/机型/飞行时长 整理进事件备注(update/create 的备注参数),航站楼进地点。不录乘客/机票号/订单号(没必要、有隐私顾虑)。 - 提醒标题加区分后缀,让用户在清单里一眼看清:
✈️ MU5137 值机打包(前1天)、🚄 G1234 该去车站了(前1h)、⚖️ 开庭准备(前3h)。 - 脚本会自动降级:未装
remindctl时用osascript(提醒仍能创建,只是不能查/改/删)。
第 5 步:回执
给用户一个简短清单,例如:
已为你安排 ✈️ MU5137(2026-07-20 08:30,上海浦东 T2)
📅 日历事件已建 →「个人」日历
⏰ 提醒事项已建 3 条:
• 07-19 09:00 值机打包(前1天)
• 07-20 05:30 该去机场了(前3h)
• 07-20 07:30 开始登机(前1h)
(经 iCloud 同步到你的 iPhone/iPad,稍等片刻可在手机上看到)
更新已有事件时,回执换成「改了什么」,例如:
✈️ MU5137 已改签为 08:50 起飞——更新的是你 7/20 已有的那条日历事件(未新建)
📅 改动:开始时间 08:30 → 08:50,备注补充了舱位信息
首次使用配置(重要)
- 若
config/config.json还不存在,从模板复制一份:cp config/config.example.json config/config.json - 跑自检,拿到你机器上真实的日历名和提醒清单名:
bash $SKILL_DIR/scripts/setup_check.sh - 把输出里的日历名填进
config.json的default_calendar,清单名填进default_reminder_list。填错不会报错——脚本会兜底落到第一个日历/默认清单,但名字对才会落到你想要的地方。
config.json是你的本地配置(被 .gitignore 忽略、不提交);config.example.json是入库的默认模板。
想调整各类事件的提前量,直接改 config.json 的 lead_times(格式见 references/lead-times.md)。
提前量就是个人配置入口:用户表达「提醒太多/只要提前 3 小时/前 1 天不用提醒」这类偏好时,把它写进 config.json 的 lead_times(一次配置长期生效),不要每次口头忽略默认——否则下次换会话又会按默认建一串。
安装更强后端(可选)
提醒事项默认用 osascript(只能创建)。装上 remindctl 后还能查/改/删:
brew install steipete/tap/remindctl
装不装不影响创建功能,本 skill 会自动探测。
脚本参考
| 脚本 | 作用 |
|---|---|
scripts/create_event.sh |
建日历事件(osascript) |
scripts/find_event.sh |
按日期窗口+关键词查已有事件(查重/定位,输出 uid 和各字段) |
scripts/update_event.sh |
修改已有日历事件(按 UID 或标题定位,- = 不改该项;create_event.sh 只能新建) |
scripts/create_reminder.sh |
建提醒(remindctl 优先,降级 osascript) |
scripts/list_calendars.sh |
列出所有日历名 |
scripts/list_reminder_lists.sh |
列出所有提醒清单 |
scripts/setup_check.sh |
首次自检:依赖、权限、日历/清单名 |
修改已有事件(用 update_event.sh,别用 create_event.sh 重建)
用户说「把行程加到我已有的那个日程上」「改一下时间」「补上酒店信息」时,是修改不是新建。
create_event.sh 只会新建,重复调用会产生重复事件——这类需求一律走:
$SKILL_DIR/scripts/update_event.sh "<UID 或 标题关键词>" ["<新标题>"] ["<开始>"] ["<结束>"] ["<地点>"] ["<备注>"]
参数规则:第 2–6 个参数传 - 表示「不改这一项」;开始/结束可只改其一,另一个填 -。
定位顺序:先按 UID 精确匹配 → 匹配不到再按标题包含匹配 → 标题匹配到多个时拒绝修改并返回候选清单(防误改),此时应改用 UID。
先查再改:不知道 UID 时,先用 osascript 按关键词把候选事件(含 UID、起止、日历名)列出来,再按 UID 精确改。
例:把已有的「西安出差」占位改准,并补酒店信息
update_event.sh "西安出差" "-" "2026-09-08 10:10" "2026-09-09 12:35" "西安 · XX大学" "去程 CA1234 …"
update_event.sh "XX酒店" "-" "-" "-" "-" "预订号 888888 · 客户经理 …"
⚠️ AppleScript 改日期的两个坑(已踩过)
- 组件顺序:必须先
set day of d to 1,再依次设year / month / day,最后设hours / minutes / seconds。顺序反了可能因目标月天数不足而溢出(例如把 31 日塞进 9 月)。 - 禁止用减法跨天:想表示「9 日 17:00」就直接设
day=9, hours=17;不要写成day=9, hours=0再减 7 小时——减法会跨天回退到 8 日 17:00,把两天的行程压进同一天。
改完务必用 osascript 复核一遍起止时间,确认跨天事件真的跨了天。
注意
- 这是改用户个人日历/提醒的操作。创建是安全的、可逆的(用户可在 App 里删);但如本 skill 未来扩展删除/批量操作,务必先和用户确认目标。
- 提醒数据是个人隐私,不要把日历/提醒内容发到网络或第三方服务。解析只在本地完成。
- 若用户其实想要的是飞书/钉钉/Outlook 等非苹果日历,本 skill 不适用——那些需要各自的工具。