一、整体导读

本文围绕 OpenClaw 平台下 Apple Reminders 提醒事项集成技能展开全方位功能打磨与能力拓展。原生版本仅实现基础的事项读取、新建等简易操作,功能单一、联动性弱、交互体验平淡,仅能完成最基础的指令调用,无法挖掘 Apple 原生提醒事项在多端同步、智能分类、周期任务、场景联动上的潜力。

本次开发跳出基础封装逻辑,从调用链路、数据处理、交互体验、智能规则、生态联动、异常防护六大方向逐层优化,拆解多项可落地的改造细节,配套分层代码片段、完整整合源码、分步调试流程与进阶拓展思路。模块之间相互解耦,支持局部微调、功能叠加、个性化定制,既能满足日常轻量化使用,也可适配自动化流程、多设备协同办公等复杂场景。

二、原生技能现存短板梳理

结合本地运行、跨设备同步、日常任务管理、OpenClaw 联动场景,梳理原生技能在功能、交互、稳定性、拓展性上的各类问题,作为本次优化迭代的核心切入点:

  • 调用方式固化:硬编码系统调用指令,不区分 macOS/iOS 运行环境,未适配不同系统版本的 Apple Reminders 接口差异,高版本系统易出现调用失效。
  • 字段支持不全:仅支持标题录入,无法设置截止时间、优先级、提醒日期、标签、清单分组,无法发挥原生提醒事项的分类管理能力。
  • 查询能力单薄:仅支持全量列出事项,无关键词检索、状态筛选、分组过滤、时间范围筛选,清单数量较多时查找效率极低。
  • 缺少编辑与闭环能力:仅支持新增、查看,不支持修改内容、标记完成、恢复待办、删除事项,无法完成完整的任务生命周期管理。
  • 周期任务空白:不支持重复提醒、每日/每周/每月循环事项创建,无法适配打卡、定时提醒、周期性工作安排等场景。
  • 数据展示粗糙:原始数据直接输出,无格式美化、无状态标注、无时间格式化,阅读体验差,信息层级混乱。
  • 会话与权限管控缺失:未做权限预校验、环境检测,系统隐私权限未开启、应用未授权时仅返回原始报错,无法快速定位问题。
  • 无本地缓存与状态记忆:每次调用都重新拉取全量数据,重复请求造成资源浪费,也无法记录操作历史、追溯任务变更。
  • 生态联动不足:指令形式生硬,无法和日程、语音播报、自动化触发器联动,难以融入 OpenClaw 整体工作流。
  • 并发调用无管控:短时间多次发起查询、新增指令,易造成系统接口拥堵、数据错乱,缺少请求排队与频率限制。

三、功能进化核心方向

  1. 环境适配优化:区分 macOS、移动端运行环境,适配多系统版本调用规则,提升跨平台兼容性。
  2. 全字段能力补齐:开放时间、优先级、标签、分组、备注等原生字段,完整还原 Apple Reminders 配置能力。
  3. 多维检索体系搭建:支持关键词、状态、分组、时间区间多条件筛选,提升事项查找效率。
  4. 任务全生命周期管理:补充编辑、完成、撤销、删除功能,实现事项从创建到归档的闭环管理。
  5. 周期提醒拓展:新增循环任务配置,支持日/周/月周期性待办创建。
  6. 数据格式化输出:统一排版、状态标识、时间转换,优化内容展示效果。
  7. 权限与异常精细化处理:前置环境、权限检测,将原始报错转化为易懂提示,降低排错成本。
  8. 本地缓存与操作日志:增加数据缓存、操作记录,减少重复请求,实现行为可追溯。
  9. 请求队列管控:增加并发限制、任务排队机制,保障高频调用下的数据稳定性。
  10. 自然交互与生态联动:优化指令语义,支持跨技能联动,打通自动化场景。

四、运行环境与模块化架构

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)}`);
    }
  }
};

七、分步落地部署指南

  1. 权限配置:打开 macOS 系统设置,进入「隐私与安全性-自动化」,允许当前终端/客户端访问提醒事项。
  2. 源码替换:删除原生技能 index.ts 文件,将上方完整代码粘贴为新文件。
  3. 参数适配:根据设备修改 REMINDER_CONFIG 中的运行平台、默认分组、缓存时长。
  4. 目录校验:代码会自动生成缓存、日志文件夹,无需手动创建。
  5. 重载技能:在 OpenClaw 内重载当前技能,完成初始化。
  6. 功能测试:依次测试新建、查询、完成、删除、分组切换等全部功能。

八、后续进阶拓展方向

  • 新增周期性循环提醒,支持每日、每周、每月重复任务创建
  • 增加优先级标签管理,区分高/中/低优先级事项
  • 联动语音类技能,新增事项自动语音播报提醒
  • 增加批量导入、批量完成、批量删除功能,适配大批量任务管理
  • 增加时间范围筛选,按今日/本周/未来七日筛选待办
  • 对接自动化触发器,实现定时自动创建、自动提醒
  • 增加数据导出能力,将提醒清单导出为本地文本文件

九、整体优化亮点总结

本次优化跳出原生简单封装的局限,从权限、调用、字段、检索、状态管理、缓存、日志、并发、交互多个维度完成全面升级。模块拆分清晰,既可以整体替换部署,也能单独抽取代码片段对原有技能做局部微调。完整还原 Apple Reminders 核心能力,同时结合 OpenClaw 平台特性优化交互与稳定性,兼顾个人日常待办管理、周期性事务安排、多分组分类办公等场景,预留充足拓展空间,可根据个性化需求持续迭代出新功能。