
1. 项目起因Raycast 卸载后为什么还有一堆残留文件事情的起因很朴素我打算把主力 Mac 从 Raycast 切回原生 Spotlight 自定义快捷键方案顺手就把 Raycast 从“应用程序”文件夹拖进了废纸篓。本以为这就算卸载完了结果用磁盘工具一扫发现~/Library/Application Support/Raycast、~/Library/Caches/com.raycast.macos、~/Library/Preferences/com.raycast.macos.plist这些目录全都还在加起来少说有几百 MB。更麻烦的是Raycast 的登录启动项也悄悄留在~/Library/LaunchAgents里每次开机虽然不弹窗但后台进程还活着日志还在写。这个问题的本质是 macOS 应用卸载机制太“宽松”了。平时从访达里把 .app 拖进废纸篓系统只会删除可执行文件本身而应用运行期间写入用户资源库~/Library的各种数据一概不管。对于一个重度使用、有几十个扩展插件、存了剪贴板历史、配置了无数快捷键的 Raycast 来说残留数据的体积和数量都是相当大的。手动去翻目录一个个删效率低不说还容易漏。更怕的是误删了别的应用共用的缓存目录比如~/Library/Caches下面有些条目是多个应用共享写入的瞎删有可能把别的应用搞崩。于是我决定把清理流程彻底梳理一遍做成一个开源的 Skill —— 它的定位是给 AI 编程助手、自动化工具链用的“可复用技能包”。简单说Skill 就是把一套清理逻辑、脚本、清单和说明文档打包让别人可以直接调用不用再自己从头排查 Mac 的各个目录该删哪些。这篇文章就把整个项目的设计思路、实现细节和踩坑过程完整写出来适合遇到同类卸载残留问题的人也适合想了解 Skill 这种新形态工具该怎么做的小伙伴参考。2. 整体设计与方案选型为什么用 Skill 而不是写死一个清理脚本2.1 明确 Skill 的定位和适用场景先说结论这个项目不是要做成一个图形化清理软件也不是简单的.sh脚本丢到 GitHub 上就算了而是做成一个“Skill”。Skill 这个概念最近在 AI 编程助手和自动化工作流里非常火你可以把它理解为一种封装好的“能力单元”有清晰的描述信息、可执行的脚本、必要的配置清单可以被 AI Agent 或自动化平台动态加载和调用。我当时选 Skill 这个形态核心原因是这个场景特别适合“半自动 人机协作”卸载残留清理本身有一定风险全自动脚本直接把目录删了一旦误判就可能毁掉用户数据但完全手动又太慢。Skill 的方式是让 AI 先读取残留清单生成清理建议再让用户确认执行整个过程既高效又可控。这比写死一个上来就rm -rf的脚本要稳妥得多也比让用户自己去翻一堆目录要聪明得多。2.2 确定清理范围和需要处理的目录类型Mac 上应用残留的分布是有规律可循的搞清楚这些位置才能真正设计好清理逻辑。我梳理了 Raycast 这类 GUI 应用最常见的残留目录把它们分成四类第一类是应用支持文件也就是~/Library/Application Support/Raycast/这里面存了扩展配置、剪贴板历史数据库、扩展缓存、主题资源等等是残留的大头。第二类是缓存文件散落在~/Library/Caches/com.raycast.macos/和~/Library/WebKit/com.raycast.macos/这类带 Bundle ID 的目录里。第三类是偏好设置主要是~/Library/Preferences/com.raycast.macos.plist有时候还会连带产生~/Library/HTTPStorages/和~/Library/Saved Application State/下面的同名文件。第四类是启动代理和日志~/Library/LaunchAgents/下面如果放了 plist 文件说明应用注册了开机自启这个不删的话后台进程会一直存在。为什么是这四类而不是更多因为对于 Raycast 这种现代沙盒化程度不高的 macOS 应用来说运行期能写文件的位置基本就在用户资源库范围内系统级目录如/Library一般需要管理员权限普通应用写不进去所以基本可以排除。真正需要担心的反而是有些应用会在~/Library/Group Containers下创建共享容器目录Raycast 其实也用到了这个目录特点是 Bundle ID 带 team 前缀删除前必须确认没有其他应用在用否则会影响同组的其他应用。2.3 为什么把流程化而非脚本化作为核心设计原则我在一开始设计的时候其实走过一段弯路想着直接写一个清理脚本检测到残留目录就删除一步到位。后来在本地测试时发现这么做有两个致命问题。第一个是误删除风险无法控制比如用户如果同时装了 Raycast 的 Safari 扩展或者菜单栏工具直接删掉整个 Application Support 目录会把这些扩展的独立数据也带走。第二个是审计和回滚无从谈起脚本删了就删了没有操作记录用户也不知道系统被改了什么。所以我把核心设计原则改为“流程化”先扫描再展示然后确认最后执行并生成报告。Skill 里包含扫描脚本、清理脚本、配置文件和使用说明AI 在调用时读取说明后按流程执行 —— 先让扫描脚本输出一份残留清单AI 根据清单判断哪些可以安全删除哪些需要用户确认最后执行删除时还会先把关键配置备份到临时目录。这个“人机协作 可审计”的思想比单一脚本要可靠得多。3. 核心细节与实现要点扫描、备份、清理三步走3.1 残留扫描模块的具体实现扫描是清理的第一步也是最需要严谨的一步。我写了一个scan.sh逻辑上先收集 Raycast 相关的所有可能残留路径然后挨个检查是否存在再统计每个目录的磁盘占用。路径列表是维护在一个paths.txt配置里的这样以后要支持清理其他应用只要改配置文件就行不需要改脚本本身。核心扫描逻辑非常简单就是文件系统存在性判断加上du统计。我直接用了find的深度限制避免扫出太深层级的碎片文件同时又用du -sh对每个候选路径求占用空间。扫描结果输出成 JSON 格式因为后面 AI 要读取这个结果来做判断JSON 比纯文本格式更结构化、更不容易被解析错。# 扫描核心逻辑示意 check_path() { local path$1 if [ -e $path ]; then local size$(du -sh $path 2/dev/null | awk {print $1}) echo {\path\:\$path\,\exists\:true,\size\:\$size\} fi }这里有个细节值得单独说明du -sh对某些特殊文件比如数据库文件在被占用时可能读取失败所以我在命令后面加了2/dev/null并且在解析结果时默认让 AI 把这些文件视为“存在但无法统计大小”。这样的容错处理在实际操作里很关键因为 Raycast 的剪贴板历史数据库经常被系统进程占用扫描时偶尔会遇到权限报错。3.2 备份机制让删除操作可回滚清理操作最让人不放心的就是删错了怎么办。为了给用户“后悔药”我在清理前增加了一个备份步骤把待删除的目录先复制到系统临时目录下的一个带时间戳的文件夹里复制成功后才执行删除。备份目录保留 48 小时期间用户如果发现异常可以直接恢复。备份的实现用了ditto而不是cp -R区别在于ditto能保留文件的扩展属性和资源派生数据。在 macOS 上删除应用残留后需要恢复时属性丢失不会导致太大问题但既然要做得专业保留元数据总归是更稳妥的选择。备份时也对大目录做了一些过滤 —— 比如 Raycast 的剪贴板历史数据库可能上 GB如果直接全量复制会让备份过程和网络传输一样慢所以我默认备份时跳过体积超过 500MB 的目录只在报告中提示用户这些目录已直接删除且无法回滚。注意备份不是万能保险。如果你要用这个 Skill 清理其他应用请先确认目标应用没有正在运行的进程否则即使备份了也不能保证恢复后数据完整。3.3 清理动作的优先级和依赖处理清理不是“检测到目录就删”这么简单。我根据各目录之间的依赖关系把清理动作分了两个阶段。第一阶段删除的是纯缓存类数据~/Library/Caches/、~/Library/WebKit/、~/Library/HTTPStorages/这些数据删了最多就是应用下次启动时重新生成没有任何风险。第二阶段删除的是配置和数据类~/Library/Application Support/、~/Library/Preferences/、~/Library/LaunchAgents/这些才是真正影响“卸载干净”的目录。为什么第二阶段必须放到最后因为有些应用在运行时会把内存中的状态定期写回配置文件和 Application Support如果你反向操作、先把配置目录删了应用即使没在运行也会有残留的辅助进程在下一分钟重新把配置目录建出来。实际测试中我就遇到过删了~/Library/Application Support/Raycast之后一个没被关闭的 Raycast 辅助进程又把它重建了。所以清理的前置条件一定是先退出所有相关进程这一点我在 Skill 的说明文档里专门加粗提醒。3.4 Skill 的配置文件设计如何让 AI 正确理解清理任务Skill 除了要有可执行的代码更重要的是让 AI 能理解“什么时候该调用、怎么调用、边界是什么”。我设计了一个SKILL.md文件内容包含三块技能描述、调用条件和执行步骤。技能描述里明确指出这个 Skill 是“用于安全清理 macOS 应用的卸载残留当前支持 Raycast”并给出了适用场景和不适用的场景比如不能用于清理系统级应用不能用它来释放磁盘空间做激进优化。调用条件部分则定义了触发词当用户提到“Raycast 卸载残留”“Mac 清理残留”等表述时AI 才应该考虑调用这个 Skill。执行步骤部分的说明非常口语化但逻辑严密大致是先运行扫描脚本等待 JSON 结果然后根据结果里的路径列表逐个判断是否属于用户明确要清理的应用确认后就执行备份和清理脚本最后把报告展示给用户。我把这些写清楚本质上是在“培训”AI 按一套稳定的流程操作而不是自由发挥。4. 实操过程与完整复现从本地测试到开源发布4.1 搭建项目结构与初始化整个项目的结构非常清爽我最终采用的是分层布局assets目录放图标和演示截图scripts目录放可执行脚本docs目录放使用说明和常见问题根目录放SKILL.md和README.md。这样设计的好处是以后扩展支持更多应用时每个应用只要在scripts/apps/下增加一个配置子目录就行不影响整体框架。初始化项目的时候我顺手把 GitHub 仓库创建好README 里放了一个醒目的“风险提示”徽章然后写清楚了适配的 macOS 版本项目在 macOS 13/14/15 上测试通过、依赖要求需要安装好命令行开发者工具zsh作为默认 shell、以及使用方法。我建议所有想用这个 Skill 的人都先去看看docs/FAQ.md里面整理了我在本地实验时踩过的四类坑提前了解可以帮助你减少一些无谓的试错。4.2 本地环境准备与脚本可执行权限设置Skill 里的脚本要能在 Mac 上直接运行第一步是给脚本加执行权限。虽然 AI 调用时理论上可以用bash scripts/scan.sh的方式绕开权限问题但为了兼容不同调用方式我统一把scripts/*.sh都加了chmod x。另外因为项目开源下载到本地后文件权限属性可能被 Git 重置所以我在 README 里专门写了一条初始化命令chmod x scripts/*.sh这个细节看起来不起眼但实际使用中如果漏掉会遇到Permission denied报错非常影响体验。我就是因为一开始没写进文档有用户提 issue 说脚本跑不起来排查半天才发现是权限问题。4.3 扫描与清理脚本的联调测试我在本地反复测试了几轮扫描、备份、清理的完整流程。测试过程中重点观察三个指标扫描结果是否完整、备份过程是否因权限中断、清理后是否有目录被进程重新创建。第一轮测试就发现 Raycast 的Saved Application State目录扫描结果不稳定有时存在有时不存在后来搞清楚是因为该目录在应用退出后才会持久化写入应用运行期间它是不稳定的临时状态文件。所以在扫描配置里我特别注意了这个路径的语义 —— 它的存在不代表一定有残留还要结合应用进程是否退出来判断。清理脚本的执行过程我设计了打印输出每个阶段会打印[1/3] Scanning、[2/3] Backing up、[3/3] Cleaning这样的步骤提示方便在日志里追踪进度。脚本执行结束之后会把清理结果写到一个report.json里内容包括清理了哪些路径、释放了多少空间、备份目录在哪。4.4 开源发布与 Skill 导入方式项目在 GitHub 上开源后使用方式有两种。一种是纯手动方式把仓库 clone 到本地按 README 里的说明手动执行扫描和清理脚本适合只想自己清理 Mac 的用户。另一种是 Skill 方式把仓库里的SKILL.md和scripts目录复制到 AI 编程助手的 Skill 目录下比如~/path/to/your/ai/skills/然后 AI 在对话过程中就能根据用户输入自动识别并调用。发布开源还有一个特别重要的点是选择开源协议。我选了 MIT 协议因为这种工具类项目希望被尽量多人使用和修改MIT 协议足够宽松别人拿去集成到自己的工具链也不会有什么法律阻碍。同时在 README 里加了完整的免责声明 —— 清理操作涉及删文件任何误删都有可能发生项目只提供流程和代码使用后果需要自己承担。5. 常见问题与排查技巧实录5.1 扫描不到残留但确实感觉系统变慢了这个问题听起来有点玄但实际原因非常具体。我在测试中也遇到过几次扫描脚本提示“没有匹配到 Raycast 残留目录”但 Activity Monitor 里明明还看得到 Raycast 相关的进程在跑。排查方向其实不是文件目录而是 launchd 服务。Raycast 安装时会注册一个 LaunchAgent它的 plist 文件描述不一定是com.raycast.macos.plist也可能是com.raycast.RaycastHelper.plist之类的辅助进程。后来我在扫描配置里补充了模糊匹配逻辑不仅检查精确路径还会在~/Library/LaunchAgents目录下用grep搜索包含raycast关键字的 plist 文件。这样就能捞出那些“披着马甲”的残留进程。这个思路后来扩展到了所有类型目录搜索结果中只要包含raycast关键字不区分大小写就会标记为候选残留。5.2 清理过程中提示文件被锁定无法删除macOS 上删除文件最常见的失败原因就是文件被某个进程占用。尤其是在清理 WebKit 缓存目录时即使你已经退出了 Raycast 主程序系统的com.apple.WebKit.WebContent进程可能还在持有这些缓存文件。解决方式是把相关进程先处理掉pkill -f Raycast 2/dev/null || true我在清理脚本第二阶段之前强制执行了这一步。但这里有个需要注意的操作习惯pkill -f Raycast是一个按名字模糊匹配的强杀命令如果系统里有其他名字里带 Raycast 的应用也会被误杀。所以实际执行时我换成了先获取准确进程列表、再针对 Bundle ID 去杀进程的方式只通过pgrep配合ps来精确匹配。还有一部分文件是因为文件权限变成只读导致的这种情况通常需要用到管理员权限。Skill 里默认不做sudo操作因为需要输入密码的流程会打断自动化调用。我的做法是在文档里提示用户如果遇到Operation not permitted可以自行用访达进入对应目录查看权限或者手动用sudo rm删除。5.3 清理后某个系统功能出现异常这里有几种情况需要区分。如果是清理完 Raycast 残留后出现 Spotlight 搜索卡顿这不一定是清理的锅而是 Spotlight 索引重建的正常过程。删除了大量文件后Spotlight 需要重新索引过程会占用一些 CPU。遇到这种情况不用慌等它索引完就恢复正常了。比较危险的情况是用户误用这个 Skill 去清理其他应用比如把某个依赖共享框架的应用数据目录直接干掉导致该应用启动报错。我在 Skill 描述里明确了边界当前版本只适配 Raycast 相关路径对于其他应用需要先把路径配置补充到paths.txt里并经过本地测试验证后才可使用。这个限制不是偷懒而是对使用者负责。5.4 常见问题速查表现象可能原因解决方式脚本报 Permission denied缺少执行权限执行chmod x scripts/*.sh扫描不到残留残留进程导致目录重建 / 辅助进程注册名不同先退出全部关联进程再按关键字模糊搜索删除时提示文件被占用进程未完全退出或系统扩展持有文件用pkill精确匹配或重试删除清理后部分设置未恢复备份目录中存在但恢复时遗漏检查备份目录完整结构手动恢复缺失文件想清理其他应用但没配置文件Skill 内置清单只覆盖 Raycast参考 repository 里的 schema 自行补充 paths.txt清理后 Spotlight 变慢系统正在重建索引等一段时间属于正常现象6. Skill 的边界、扩展方向与个人实操心得6.1 当前版本的限制和可以继续扩展的方向我清楚这个 Skill 目前只是一个小工具但它设计的框架是通用的。以后如果要支持 AppCleaner 清理的同类应用只要维护每个应用的路径配置文件就行。再进一步可以做依赖分析 —— 通过解析 plist 里的LSApplicationDependencies或者sharedfilelist来识别哪些目录是“当前应用独有”的哪些是和其他应用共享的这样清理逻辑会更聪明不会误伤共享数据。另外的方向是把备份策略做得更智能化现在我是按目录大小来决定是否备份但这不够精确。更好的方案是扫描时计算每个目录内文件的访问时间和修改时间如果发现一个目录最近一周都没有被访问过那备份优先级就可以降低甚至跳过。这样能在数据安全和清理效率之间找一个更优的平衡点。我在开源项目的 TODO 里已经写上了这个想法后续计划持续迭代。6.2 我在实际开发中的三个关键体会第一个体会是做这种清理类工具最核心的功夫其实不是写删除逻辑而是穷举残留路径。一个应用在系统里留下多少痕迹单靠经验猜是不准确的必须在安装前、使用后、卸载后分别对比系统目录快照用最笨的方法摸清家底。Raycast 的残留路径清单我是实测了三次每次都有新发现 —— 第一次漏了 WebKit 目录第二次漏了 Group Containers第三次才凑齐完整列表。第二个体会是Skill 的描述文档写得越“笨”AI 调用时越不容易出错。为了让 AI 能正确处理我把路径列表、执行顺序、备份策略都写在配置文件里并对每个字段加了注释。AI 读取这些内容后能形成自己的执行计划而不是靠瞎猜。这有点像给一个新同事写一份非常详细的工作手册手册里的每个句子都要尽量不含歧义。第三个体会是关于开源的项目虽然不大但发布后收到的反馈比我预期的多。有用户提出希望支持清理时同时清理终端的 shell 历史记录有人建议加入定时清理的计划任务能力还有人问能不能做一个 App Cleaner 的 Skill 版本。这些反馈让我意识到大家真正需要的不是一个“一次性清理工具”而是一套可以复用的、可扩展的清理流程框架。下一步我会按照这个思路补强基础设施把针对单应用的路径配置做成更易共享和演进的格式。我自己平时清理 Mac 的经验是不要等到出了问题才想起来清理卸载一个应用之后顺手跑一遍清理流程应该变成肌肉记忆。用这个 Skill 时建议你第一次使用先只跑扫描把残留清单导出来看一眼对系统里这些目录的位置和大小有一个基本感觉之后再做备份和清理。手动执行也好交给 AI 自动调用也好最终目的都是让自己的系统保持清爽可控。