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.md
source content

finder-action — Mac Finder 右键菜单动作生成器

根据用户描述生成 Finder 右键菜单动作,自动判断模式:

场景模式技术方案
右键文件/文件夹Quick ActionAutomator workflow
右键空白处Finder ExtensionSwift + xcodegen

参数格式

<动作名称> [触发描述]

示例:

  • pdf2png .pdf 将PDF所有页面纵向拼接成一张PNG
    → Quick Action
  • 新建md文件 在空白处右键创建markdown文件
    → Finder Extension

模式判断

关键词命中 → 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

关键配置:

  • inputMethod
    :
    1
  • serviceInputTypeIdentifier
    :
    com.apple.Automator.fileSystemObject
  • workflowTypeIdentifier
    :
    com.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:
    LSUIElement: true
    (无 Dock 图标)
  • Extension:
    NSExtensionPointIdentifier: com.apple.FinderSync
  • 签名:
    CODE_SIGN_IDENTITY: "-"
    (ad-hoc)
  • 沙盒必须开启
    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
  • 执行脚本
    Process()
    启动 shell 命令

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
    item.target = self
    ,否则 action 不会触发
  • Finder Extension 菜单项位置由系统决定,无法排在「新建文件夹」之前
  • Quick Action 环境没有
    $PATH
    ,工具路径必须用绝对路径
  • Automator workflow 需在 Automator 中打开保存才能注册
  • Extension 使用 ad-hoc 签名,仅限本机使用