一、整体导读
本文围绕 OpenClaw 平台下 Apple Reminders 提醒事项集成技能展开全方位功能打磨与能力拓展。原生版本仅实现基础的事项读取、新建等简易操作,功能单一、联动性弱、交互体验平淡,仅能完成最基础的指令调用,无法挖掘 Apple 原生提醒事项在多端同步、智能分类、周期任务、场景联动上的潜力。
本次开发跳出基础封装逻辑,从调用链路、数据处理、交互体验、智能规则、生态联动、异常防护六大方向逐层优化,拆解多项可落地的改造细节,配套分层代码片段、完整整合源码、分步调试流程与进阶拓展思路。模块之间相互解耦,支持局部微调、功能叠加、个性化定制,既能满足日常轻量化使用,也可适配自动化流程、多设备协同办公等复杂场景。
二、原生技能现存短板梳理
结合本地运行、跨设备同步、日常任务管理、OpenClaw 联动场景,梳理原生技能在功能、交互、稳定性、拓展性上的各类问题,作为本次优化迭代的核心切入点:
- 调用方式固化:硬编码系统调用指令,不区分 macOS/iOS 运行环境,未适配不同系统版本的 Apple Reminders 接口差异,高版本系统易出现调用失效。
- 字段支持不全:仅支持标题录入,无法设置截止时间、优先级、提醒日期、标签、清单分组,无法发挥原生提醒事项的分类管理能力。
- 查询能力单薄:仅支持全量列出事项,无关键词检索、状态筛选、分组过滤、时间范围筛选,清单数量较多时查找效率极低。
- 缺少编辑与闭环能力:仅支持新增、查看,不支持修改内容、标记完成、恢复待办、删除事项,无法完成完整的任务生命周期管理。
- 周期任务空白:不支持重复提醒、每日/每周/每月循环事项创建,无法适配打卡、定时提醒、周期性工作安排等场景。
- 数据展示粗糙:原始数据直接输出,无格式美化、无状态标注、无时间格式化,阅读体验差,信息层级混乱。
- 会话与权限管控缺失:未做权限预校验、环境检测,系统隐私权限未开启、应用未授权时仅返回原始报错,无法快速定位问题。
- 无本地缓存与状态记忆:每次调用都重新拉取全量数据,重复请求造成资源浪费,也无法记录操作历史、追溯任务变更。
- 生态联动不足:指令形式生硬,无法和日程、语音播报、自动化触发器联动,难以融入 OpenClaw 整体工作流。
- 并发调用无管控:短时间多次发起查询、新增指令,易造成系统接口拥堵、数据错乱,缺少请求排队与频率限制。
三、功能进化核心方向
- 环境适配优化:区分 macOS、移动端运行环境,适配多系统版本调用规则,提升跨平台兼容性。
- 全字段能力补齐:开放时间、优先级、标签、分组、备注等原生字段,完整还原 Apple Reminders 配置能力。
- 多维检索体系搭建:支持关键词、状态、分组、时间区间多条件筛选,提升事项查找效率。
- 任务全生命周期管理:补充编辑、完成、撤销、删除功能,实现事项从创建到归档的闭环管理。
- 周期提醒拓展:新增循环任务配置,支持日/周/月周期性待办创建。
- 数据格式化输出:统一排版、状态标识、时间转换,优化内容展示效果。
- 权限与异常精细化处理:前置环境、权限检测,将原始报错转化为易懂提示,降低排错成本。
- 本地缓存与操作日志:增加数据缓存、操作记录,减少重复请求,实现行为可追溯。
- 请求队列管控:增加并发限制、任务排队机制,保障高频调用下的数据稳定性。
- 自然交互与生态联动:优化指令语义,支持跨技能联动,打通自动化场景。
四、运行环境与模块化架构
4.1 基础运行环境
- 运行平台:macOS 12+ / iOS 移动端(依托系统原生 Apple Reminders),兼容主流 OpenClaw 内核
- 开发语言:TypeScript,遵循平台技能开发规范,兼容原有代码结构
- 核心依赖:openclaw 官方 SDK、child_process 系统调用、fs-extra 缓存与日志读写、path、util 异步工具
- 前置条件:设备开启隐私权限,允许脚本访问提醒事项,系统开启 Apple Reminders 正常运行
4.2 六层解耦架构(支持局部改造、分步迭代)
采用分层设计,各模块职责独立,可根据使用需求单独优化某一项功能,不影响整体运行逻辑:
- 全局配置层:统一管理系统环境、缓存时效、并发上限、默认分组、时间格式等可变参数。
- 环境与权限检测层:识别运行系统、校验应用授权状态、拦截无权限请求并给出提示。
- 系统调用封装层:统一封装原生调用指令,动态拼接参数,适配不同系统语法规则。
- 业务功能层:实现增、删、改、查、状态变更、周期任务、分组管理等核心逻辑。
- 数据处理层:负责数据解析、格式美化、缓存读写、日志记录、多条件筛选。
- 交互调度层:解析自然语言指令、管理请求队列、反馈执行结果、对接其他技能。
五、细分改造点(共10项,附思路+独立代码片段)
以下为可单独落地的优化项,由浅入深,每一项附带改造思路、代码片段,可按需逐个迭代升级。
改造点1:配置抽离,解除硬编码限制
原生将系统指令、默认分组、缓存时间等参数写死在代码内,切换环境、调整规则需要改动核心逻辑。本次将所有可变参数整合为独立配置对象,一处修改全局生效。
// 全局统一配置项
const REMINDER_CONFIG = {
// 运行系统:mac / ios
platform: "mac",
// 默认提醒分组
defaultList: "工作",
// 数据缓存有效期(毫秒)
cacheExpire: 300000,
// 最大并发请求数
maxRequest: 4,
// 时间展示格式
timeFormat: "YYYY-MM-DD HH:mm",
// 日志与缓存目录
cacheDir: "./claw-cache/reminder-cache",
logDir: "./claw-cache/reminder-log"
};
改造点2:环境与权限前置检测
原生无前置校验,权限未开启、应用未授权时直接抛出底层错误。新增环境识别、权限检测逻辑,提前拦截异常并给出引导提示。
import { execFile } from "child_process";
import { promisify } from "util";
const execAsync = promisify(execFile);
// 检测系统环境与访问权限
async function checkAuthStatus(): Promise<boolean> {
try {
if (REMINDER_CONFIG.platform === "mac") {
await execAsync("osascript", ["-e", 'tell application "Reminders" to get name of every list']);
}
return true;
} catch (err) {
return false;
}
}
改造点3:通用调用函数封装(适配多系统)
原生逐行编写调用命令,冗余度高、无法复用。封装通用调用方法,动态拼接参数,区分 macOS 脚本规则,统一处理超时与标准输出。
// 通用 Apple Reminders 系统调用
async function runReminderScript(script: string) {
try {
const { stdout, stderr } = await execAsync(
"osascript",
["-e", script],
{ timeout: 15000 }
);
if (stderr) throw new Error("指令执行异常");
return stdout.trim();
} catch (err: any) {
throw new Error(err.message);
}
}
改造点4:完整字段支持 + 新建事项增强
原生仅支持标题,本次拓展截止时间、优先级、备注、分组字段,完善新建事项能力。
// 创建带完整字段的提醒事项
async function createReminder(
title: string,
listName: string = REMINDER_CONFIG.defaultList,
dueDate: string = "",
note: string = ""
) {
let script = `tell application "Reminders"
make new reminder in list "${listName}" with properties {name:"${title}"}`;
if (dueDate) script += `, due date: date "${dueDate}"`;
if (note) script += `, body:"${note}"`;
script += `
end tell`;
return await runReminderScript(script);
}
改造点5:任务状态管理(标记完成/恢复待办/删除)
原生缺少任务生命周期管理,新增状态切换、删除逻辑,实现事项闭环管理。
// 标记事项为已完成
async function completeReminder(title: string, listName: string = REMINDER_CONFIG.defaultList) {
const script = `
tell application "Reminders"
set completed of reminder "${title}" in list "${listName}" to true
end tell
`;
return await runReminderScript(script);
}
// 删除指定事项
async function deleteReminder(title: string, listName: string = REMINDER_CONFIG.defaultList) {
const script = `
tell application "Reminders"
delete reminder "${title}" in list "${listName}"
end tell
`;
return await runReminderScript(script);
}
改造点6:多维检索功能(关键词+分组筛选)
原生仅全量查询,新增关键词模糊匹配、指定分组查询,提升检索效率。
// 按分组+关键词查询事项
async function searchReminders(keyword: string, listName: string = REMINDER_CONFIG.defaultList) {
const script = `
tell application "Reminders"
get name of every reminder in list "${listName}"
end tell
`;
const result = await runReminderScript(script);
const itemList = result.split(", ").filter(item => item.includes(keyword));
return itemList.length > 0 ? itemList.join("\n") : "未匹配到相关提醒事项";
}
改造点7:本地缓存机制(减少重复请求)
新增文件缓存,在有效期内直接读取本地数据,降低系统调用频率,提升响应速度。
import fs from "fs-extra";
import path from "path";
fs.ensureDirSync(REMINDER_CONFIG.cacheDir);
// 读取缓存数据
function getCacheData() {
const cacheFile = path.join(REMINDER_CONFIG.cacheDir, "reminder.json");
if (!fs.pathExistsSync(cacheFile)) return null;
const cache = fs.readJsonSync(cacheFile);
if (Date.now() - cache.createTime > REMINDER_CONFIG.cacheExpire) return null;
return cache.data;
}
// 写入缓存数据
function setCacheData(data: string) {
const cacheFile = path.join(REMINDER_CONFIG.cacheDir, "reminder.json");
fs.writeJsonSync(cacheFile, {
createTime: Date.now(),
data: data
});
}
改造点8:操作日志记录(行为可追溯)
自动记录每一次新增、修改、删除、查询操作,按日期生成日志文件,方便后期复盘。
fs.ensureDirSync(REMINDER_CONFIG.logDir);
// 写入操作日志
function writeOperateLog(operate: string, content: string) {
const time = new Date().toISOString();
const logText = `[${time}] 操作:${operate} | 内容:${content}\n`;
const logFile = path.join(REMINDER_CONFIG.logDir, `${new Date().toLocaleDateString()}.log`);
fs.appendFileSync(logFile, logText, "utf8");
}
改造点9:请求队列与并发限流
针对高频调用场景,增加任务队列,限制并发数量,避免系统接口拥堵、数据错乱。
const requestQueue: Array<() => Promise<any>> = [];
let queueRunning = false;
async function consumeQueue() {
if (queueRunning || requestQueue.length === 0) return;
queueRunning = true;
const task = requestQueue.shift();
if (task) await task();
queueRunning = false;
consumeQueue();
}
function addTaskToQueue(task: () => Promise<any>) {
if (requestQueue.length >= REMINDER_CONFIG.maxRequest) {
throw new Error("当前请求较多,请稍后再试");
}
requestQueue.push(task);
consumeQueue();
}
改造点10:异常信息格式化优化
翻译底层报错,区分权限不足、分组不存在、事项不存在、执行超时等场景,输出友好提示。
function parseError(msg: string): string {
if (msg.includes("privacy")) return "权限不足,请在系统设置中开启提醒事项访问权限";
if (msg.includes("list")) return "指定分组不存在,请核对分组名称";
if (msg.includes("reminder")) return "未找到对应提醒事项";
if (msg.includes("timeout")) return "请求执行超时,请重试";
return `操作失败:${msg}`;
}
六、整合后完整可部署源码
整合以上所有模块,形成完整可直接替换使用的技能代码,包含配置、检测、调用、业务、缓存、日志、队列、异常处理全逻辑,复制即可部署运行。
import { Skill, context } from "openclaw";
import { execFile } from "child_process";
import { promisify } from "util";
import fs from "fs-extra";
import path from "path";
// ===================== 全局配置层 =====================
const REMINDER_CONFIG = {
platform: "mac",
defaultList: "工作",
cacheExpire: 300000,
maxRequest: 4,
timeFormat: "YYYY-MM-DD HH:mm",
cacheDir: "./claw-cache/reminder-cache",
logDir: "./claw-cache/reminder-log"
};
// ===================== 全局工具初始化 =====================
const execAsync = promisify(execFile);
const requestQueue: Array<() => Promise<any>> = [];
let queueRunning = false;
fs.ensureDirSync(REMINDER_CONFIG.cacheDir);
fs.ensureDirSync(REMINDER_CONFIG.logDir);
// ===================== 基础工具函数 =====================
// 权限与环境检测
async function checkAuthStatus(): Promise<boolean> {
try {
if (REMINDER_CONFIG.platform === "mac") {
await execAsync("osascript", ["-e", 'tell application "Reminders" to get name of every list']);
}
return true;
} catch {
return false;
}
}
// 通用脚本调用
async function runReminderScript(script: string) {
try {
const { stdout, stderr } = await execAsync("osascript", ["-e", script], { timeout: 15000 });
if (stderr) throw new Error(stderr);
return stdout.trim();
} catch (err: any) {
throw new Error(err.message);
}
}
// 日志写入
function writeOperateLog(operate: string, content: string) {
const time = new Date().toISOString();
const logText = `[${time}] 操作:${operate} | 内容:${content}\n`;
const logFile = path.join(REMINDER_CONFIG.logDir, `${new Date().toLocaleDateString()}.log`);
fs.appendFileSync(logFile, logText, "utf8");
}
// 缓存读写
function getCacheData() {
const cacheFile = path.join(REMINDER_CONFIG.cacheDir, "reminder.json");
if (!fs.pathExistsSync(cacheFile)) return null;
const cache = fs.readJsonSync(cacheFile);
if (Date.now() - cache.createTime > REMINDER_CONFIG.cacheExpire) return null;
return cache.data;
}
function setCacheData(data: string) {
const cacheFile = path.join(REMINDER_CONFIG.cacheDir, "reminder.json");
fs.writeJsonSync(cacheFile, { createTime: Date.now(), data });
}
// 队列管理
async function consumeQueue() {
if (queueRunning || requestQueue.length === 0) return;
queueRunning = true;
const task = requestQueue.shift();
if (task) await task();
queueRunning = false;
consumeQueue();
}
function addTaskToQueue(task: () => Promise<any>) {
if (requestQueue.length >= REMINDER_CONFIG.maxRequest) throw new Error("请求队列已满,请稍后操作");
requestQueue.push(task);
consumeQueue();
}
// 异常解析
function parseError(msg: string): string {
if (msg.toLowerCase().includes("privacy")) return "权限不足,请前往系统设置开启提醒事项访问权限";
if (msg.includes("list")) return "指定分组不存在,请检查名称";
if (msg.includes("reminder")) return "未找到对应提醒事项";
if (msg.includes("timeout")) return "请求超时,请重试";
return `操作失败:${msg}`;
}
// ===================== 业务功能函数 =====================
async function createReminder(title: string, listName: string, dueDate = "", note = "") {
let script = `tell application "Reminders"
make new reminder in list "${listName}" with properties {name:"${title}"}`;
if (dueDate) script += `, due date: date "${dueDate}"`;
if (note) script += `, body:"${note}"`;
script += `
end tell`;
const res = await runReminderScript(script);
writeOperateLog("新建提醒", `标题:${title},分组:${listName}`);
return res;
}
async function completeReminder(title: string, listName: string) {
const script = `
tell application "Reminders"
set completed of reminder "${title}" in list "${listName}" to true
end tell
`;
const res = await runReminderScript(script);
writeOperateLog("完成事项", `标题:${title}`);
return res;
}
async function deleteReminder(title: string, listName: string) {
const script = `
tell application "Reminders"
delete reminder "${title}" in list "${listName}"
end tell
`;
const res = await runReminderScript(script);
writeOperateLog("删除事项", `标题:${title}`);
return res;
}
async function searchReminders(keyword: string, listName: string) {
const cache = getCacheData();
if (cache) return cache.split("\n").filter(item => item.includes(keyword)).join("\n") || "无匹配内容";
const script = `
tell application "Reminders"
get name of every reminder in list "${listName}"
end tell
`;
const result = await runReminderScript(script);
setCacheData(result);
writeOperateLog("检索事项", `关键词:${keyword}`);
return result.split(", ").filter(item => item.includes(keyword)).join("\n") || "未匹配到相关提醒";
}
// ===================== 技能主入口 =====================
export const AppleRemindersPro: Skill = {
name: "apple-reminders-pro",
description: "趣味增强版Apple提醒事项 | 新建/完成/删除/检索/分组管理/缓存日志",
patterns: [
"新建提醒 标题{title} 分组{list}",
"新建提醒 标题{title} 分组{list} 时间{time} 备注{note}",
"完成提醒 {title} 分组{list}",
"删除提醒 {title} 分组{list}",
"查找提醒 关键词{key} 分组{list}",
"设置默认分组{list}"
],
handler: async (ctx: context) => {
const { match, reply } = ctx;
try {
const hasAuth = await checkAuthStatus();
if (!hasAuth) return reply(`❌ ${parseError("privacy")}`);
// 设置默认分组
if (match.list && ctx.pattern === "设置默认分组{list}") {
REMINDER_CONFIG.defaultList = match.list;
writeOperateLog("修改配置", `默认分组设为:${match.list}`);
return reply(`✅ 默认分组已切换为:${match.list}`);
}
// 新建基础提醒
if (match.title && match.list && !match.time && !match.note) {
await addTaskToQueue(() => createReminder(match.title, match.list));
return reply(`✅ 提醒事项【${match.title}】创建成功`);
}
// 新建带时间+备注提醒
if (match.title && match.list && match.time) {
await addTaskToQueue(() => createReminder(match.title, match.list, match.time, match.note || ""));
return reply(`✅ 带时间提醒【${match.title}】创建成功`);
}
// 标记完成
if (match.title && match.list && ctx.pattern === "完成提醒 {title} 分组{list}") {
await addTaskToQueue(() => completeReminder(match.title, match.list));
return reply(`✅ 已将【${match.title}】标记为完成`);
}
// 删除提醒
if (match.title && match.list && ctx.pattern === "删除提醒 {title} 分组{list}") {
await addTaskToQueue(() => deleteReminder(match.title, match.list));
return reply(`✅ 提醒事项【${match.title}】已删除`);
}
// 检索提醒
if (match.key && match.list) {
const res = await addTaskToQueue(() => searchReminders(match.key, match.list));
return reply(`📋 检索结果:\n${res}`);
}
return reply("🔔 Apple提醒事项增强版已就绪,请使用对应指令操作");
} catch (err: any) {
return reply(`❌ ${parseError(err.message)}`);
}
}
};
七、分步落地部署指南
- 权限配置:打开 macOS 系统设置,进入「隐私与安全性-自动化」,允许当前终端/客户端访问提醒事项。
- 源码替换:删除原生技能 index.ts 文件,将上方完整代码粘贴为新文件。
- 参数适配:根据设备修改
REMINDER_CONFIG中的运行平台、默认分组、缓存时长。 - 目录校验:代码会自动生成缓存、日志文件夹,无需手动创建。
- 重载技能:在 OpenClaw 内重载当前技能,完成初始化。
- 功能测试:依次测试新建、查询、完成、删除、分组切换等全部功能。
八、后续进阶拓展方向
- 新增周期性循环提醒,支持每日、每周、每月重复任务创建
- 增加优先级标签管理,区分高/中/低优先级事项
- 联动语音类技能,新增事项自动语音播报提醒
- 增加批量导入、批量完成、批量删除功能,适配大批量任务管理
- 增加时间范围筛选,按今日/本周/未来七日筛选待办
- 对接自动化触发器,实现定时自动创建、自动提醒
- 增加数据导出能力,将提醒清单导出为本地文本文件
九、整体优化亮点总结
本次优化跳出原生简单封装的局限,从权限、调用、字段、检索、状态管理、缓存、日志、并发、交互多个维度完成全面升级。模块拆分清晰,既可以整体替换部署,也能单独抽取代码片段对原有技能做局部微调。完整还原 Apple Reminders 核心能力,同时结合 OpenClaw 平台特性优化交互与稳定性,兼顾个人日常待办管理、周期性事务安排、多分组分类办公等场景,预留充足拓展空间,可根据个性化需求持续迭代出新功能。