Skills lovstudio:finder-action
install
source · Clone the upstream repo
git clone https://github.com/lovstudio/skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/lovstudio/skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/finder-action" ~/.claude/skills/lovstudio-skills-lovstudio-finder-action && rm -rf "$T"
manifest:
skills/finder-action/SKILL.mdsource content
finder-action — Mac Finder 右键菜单动作生成器
根据用户描述生成 Finder 右键菜单动作,自动判断模式:
| 场景 | 模式 | 技术方案 |
|---|---|---|
| 右键文件/文件夹 | Quick Action | Automator workflow |
| 右键空白处 | Finder Extension | Swift + xcodegen |
参数格式
<动作名称> [触发描述]
示例:
→ Quick Actionpdf2png .pdf 将PDF所有页面纵向拼接成一张PNG
→ Finder Extension新建md文件 在空白处右键创建markdown文件
模式判断
关键词命中 → Finder Extension 模式:
- 提到「空白处」「背景」「目录背景」「新建文件」「blank space」「background」
- 动作不需要选中文件即可触发
其他情况 → Quick Action 模式
Mode A: Quick Action(Automator workflow)
Step 1: 分析需求
收集(缺失时 AskUserQuestion):
- 动作名称:右键菜单显示名
- 触发文件类型:
、.pdf
、.md
等.jpg - 核心命令:用什么工具做什么
Step 2: 检查依赖
which <所需工具>
不存在则提示
brew install <tool>。
Step 3: 生成 shell 脚本
#!/bin/bash for f in "$@"; do [[ "$f" == *.<ext> ]] || continue output="${f%.<ext>}.<out_ext>" <具体命令> "$f" -o "$output" done
- 工具路径用绝对路径(Quick Action 环境没有
)$PATH
接收文件参数(inputMethod=1)"$@"
Step 4: 创建 Automator workflow
创建
~/Library/Services/<动作名称>.workflow/Contents/document.wflow。
模板见
references/automator-template.xml。
关键配置:
:inputMethod1
:serviceInputTypeIdentifiercom.apple.Automator.fileSystemObject
:workflowTypeIdentifiercom.apple.Automator.servicesMenu
Step 5: 验证注册
plutil -lint ~/Library/Services/<动作名称>.workflow/Contents/document.wflow /System/Library/CoreServices/pbs -update killall Finder
Step 6: Automator 保存(关键)
open -a Automator ~/Library/Services/<动作名称>.workflow
必须在 Automator 中 Cmd+S 保存一次才会正式注册。
Mode B: Finder Sync Extension(Swift app)
Step 1: 检查工具链
which xcodegen && which xcodebuild
缺 xcodegen 则
brew install xcodegen。
Step 2: 创建项目结构
<ProjectName>/ ├── project.yml ├── <ProjectName>/ │ └── AppDelegate.swift └── FinderExtension/ └── FinderSync.swift
Step 3: 生成 project.yml
模板见
references/xcodegen-template.yml。替换 APP_NAME 和 BUNDLE_ID。
关键点:
- 宿主 App:
(无 Dock 图标)LSUIElement: true - Extension:
NSExtensionPointIdentifier: com.apple.FinderSync - 签名:
(ad-hoc)CODE_SIGN_IDENTITY: "-" - 沙盒必须开启(
),否则扩展不会被系统加载app-sandbox: true - 文件写入需用
,temporary-exception.files.absolute-path.read-write: [/]
无效files.user-selected.read-write
Step 4: 生成 AppDelegate.swift
import Cocoa @main class AppDelegate: NSObject, NSApplicationDelegate { func applicationDidFinishLaunching(_ notification: Notification) {} }
Step 5: 生成 FinderSync.swift
模板见
references/finder-sync-template.swift。
核心 API:
— 监控所有目录FIFinderSyncController.default().directoryURLs = [URL(fileURLWithPath: "/")]
— 空白处右键菜单menu(for: .contextualMenuForContainer)
— 获取当前目录FIFinderSyncController.default().targetedURL()
常见 action 模式:
- 创建文件:
+ 自动递增文件名FileManager.default.createFile - 打开终端:AppleScript 控制 iTerm2/Terminal(见
)references/applescript-iterm.swift - 执行脚本:
启动 shell 命令Process()
AppleScript 自动化需要在 entitlements 中添加:
com.apple.security.automation.apple-events: true
Step 6: 构建安装
xcodegen generate xcodebuild -project APP_NAME.xcodeproj -scheme APP_NAME -configuration Debug build cp -R ~/Library/Developer/Xcode/DerivedData/APP_NAME-*/Build/Products/Debug/APP_NAME.app /Applications/ open /Applications/APP_NAME.app pluginkit -e use -i BUNDLE_ID.FinderExtension
Step 7: 验证
pluginkit -m -i BUNDLE_ID.FinderExtension
不出现时指引:系统设置 → 通用 → 登录项与扩展 → 已添加的扩展 → 勾选。
沙盒限制与 Helper App 方案
Finder Sync Extension 的沙盒限制非常严格:
| 操作 | 是否允许 | 说明 |
|---|---|---|
| 写入 /tmp | ❌ | 即使添加 temporary-exception 也被阻止 |
| Process() 子进程 | ❌ | 无法启动外部命令 |
| NSAppleScript | ❌ | 无法控制其他应用 |
| NSWorkspace.open(file) | ❌ | 无法打开文件/目录 |
| NSWorkspace.open(app) | ✅ | 可以打开应用 |
| NSPasteboard | ✅ | 可以读写剪贴板 |
推荐方案:创建一个非沙盒的 Helper App,Extension 把命令放入剪贴板后打开 Helper App,由 Helper App 执行实际操作。
Helper App 示例
mkdir -p "/Applications/OpenCCHelper.app/Contents/MacOS" cat > "/Applications/OpenCCHelper.app/Contents/Info.plist" << 'EOF' <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>CFBundleExecutable</key><string>run.sh</string> <key>CFBundleIdentifier</key><string>com.lovstudio.OpenCCHelper</string> <key>LSUIElement</key><true/> </dict> </plist> EOF cat > "/Applications/OpenCCHelper.app/Contents/MacOS/run.sh" << 'EOF' #!/bin/bash CMD=$(pbpaste) osascript << APPLESCRIPT tell application "iTerm" activate tell current window create tab with default profile tell current session write text "$CMD" end tell end tell end tell APPLESCRIPT EOF chmod +x "/Applications/OpenCCHelper.app/Contents/MacOS/run.sh"
Extension 中调用:
let command = "cd '\(targetPath)' && claude" NSPasteboard.general.clearContents() NSPasteboard.general.setString(command, forType: .string) NSWorkspace.shared.open(URL(fileURLWithPath: "/Applications/OpenCCHelper.app"))
其他已知限制
- Bundle ID 不能含下划线:使用连字符或驼峰命名(
而非OpenCC
)open_cc - NSHomeDirectory() 返回容器路径:沙盒中返回
,监控目录需硬编码真实路径~/Library/Containers/<bundle-id>/Data/ - NSMenuItem 必须设置 target:
,否则 action 不会触发item.target = self - Finder Extension 菜单项位置由系统决定,无法排在「新建文件夹」之前
- Quick Action 环境没有
,工具路径必须用绝对路径$PATH - Automator workflow 需在 Automator 中打开保存才能注册
- Extension 使用 ad-hoc 签名,仅限本机使用