{"id":"8ecd100f-baa9-4d0b-963e-02e5ea102721","shortId":"GFxbW4","kind":"skill","title":"lovstudio:finder-action","tagline":"Generate Mac Finder right-click menu actions. Two modes: (A) Automator Quick Action for file/folder context menus, (B) Finder Sync Extension (Swift + xcodegen) for blank-space context menus. Automatically selects mode based on user intent. Trigger when user mentions \"Finder右键\", \"","description":"# finder-action — Mac Finder 右键菜单动作生成器\n\n根据用户描述生成 Finder 右键菜单动作，自动判断模式：\n\n| 场景 | 模式 | 技术方案 |\n|------|------|----------|\n| 右键**文件/文件夹** | Quick Action | Automator workflow |\n| 右键**空白处** | Finder Extension | Swift + xcodegen |\n\n## 参数格式\n\n`<动作名称> [触发描述]`\n\n示例：\n- `pdf2png .pdf 将PDF所有页面纵向拼接成一张PNG` → Quick Action\n- `新建md文件 在空白处右键创建markdown文件` → Finder Extension\n\n## 模式判断\n\n关键词命中 → Finder Extension 模式：\n- 提到「空白处」「背景」「目录背景」「新建文件」「blank space」「background」\n- 动作不需要选中文件即可触发\n\n其他情况 → Quick Action 模式\n\n---\n\n## Mode A: Quick Action（Automator workflow）\n\n### Step 1: 分析需求\n\n收集（缺失时 AskUserQuestion）：\n- **动作名称**：右键菜单显示名\n- **触发文件类型**：`.pdf`、`.md`、`.jpg` 等\n- **核心命令**：用什么工具做什么\n\n### Step 2: 检查依赖\n\n```bash\nwhich <所需工具>\n```\n\n不存在则提示 `brew install <tool>`。\n\n### Step 3: 生成 shell 脚本\n\n```bash\n#!/bin/bash\nfor f in \"$@\"; do\n  [[ \"$f\" == *.<ext> ]] || continue\n  output=\"${f%.<ext>}.<out_ext>\"\n  <具体命令> \"$f\" -o \"$output\"\ndone\n```\n\n- 工具路径用绝对路径（Quick Action 环境没有 `$PATH`）\n- `\"$@\"` 接收文件参数（inputMethod=1）\n\n### Step 4: 创建 Automator workflow\n\n创建 `~/Library/Services/<动作名称>.workflow/Contents/document.wflow`。\n\n模板见 `references/automator-template.xml`。\n\n关键配置：\n- `inputMethod`: `1`\n- `serviceInputTypeIdentifier`: `com.apple.Automator.fileSystemObject`\n- `workflowTypeIdentifier`: `com.apple.Automator.servicesMenu`\n\n### Step 5: 验证注册\n\n```bash\nplutil -lint ~/Library/Services/<动作名称>.workflow/Contents/document.wflow\n/System/Library/CoreServices/pbs -update\nkillall Finder\n```\n\n### Step 6: Automator 保存（关键）\n\n```bash\nopen -a Automator ~/Library/Services/<动作名称>.workflow\n```\n\n必须在 Automator 中 Cmd+S 保存一次才会正式注册。\n\n---\n\n## Mode B: Finder Sync Extension（Swift app）\n\n### Step 1: 检查工具链\n\n```bash\nwhich xcodegen && which xcodebuild\n```\n\n缺 xcodegen 则 `brew install xcodegen`。\n\n### Step 2: 创建项目结构\n\n```\n<ProjectName>/\n├── project.yml\n├── <ProjectName>/\n│   └── AppDelegate.swift\n└── FinderExtension/\n    └── FinderSync.swift\n```\n\n### Step 3: 生成 project.yml\n\n模板见 `references/xcodegen-template.yml`。替换 `APP_NAME` 和 `BUNDLE_ID`。\n\n关键点：\n- 宿主 App: `LSUIElement: true`（无 Dock 图标）\n- Extension: `NSExtensionPointIdentifier: com.apple.FinderSync`\n- 签名: `CODE_SIGN_IDENTITY: \"-\"`（ad-hoc）\n- **沙盒必须开启**（`app-sandbox: true`），否则扩展不会被系统加载\n- 文件写入需用 `temporary-exception.files.absolute-path.read-write: [/]`，`files.user-selected.read-write` 无效\n\n### Step 4: 生成 AppDelegate.swift\n\n```swift\nimport Cocoa\n\n@main\nclass AppDelegate: NSObject, NSApplicationDelegate {\n    func applicationDidFinishLaunching(_ notification: Notification) {}\n}\n```\n\n### Step 5: 生成 FinderSync.swift\n\n模板见 `references/finder-sync-template.swift`。\n\n核心 API：\n- `FIFinderSyncController.default().directoryURLs = [URL(fileURLWithPath: \"/\")]` — 监控所有目录\n- `menu(for: .contextualMenuForContainer)` — 空白处右键菜单\n- `FIFinderSyncController.default().targetedURL()` — 获取当前目录\n\n常见 action 模式：\n- **创建文件**：`FileManager.default.createFile` + 自动递增文件名\n- **打开终端**：AppleScript 控制 iTerm2/Terminal（见 `references/applescript-iterm.swift`）\n- **执行脚本**：`Process()` 启动 shell 命令\n\n**AppleScript 自动化**需要在 entitlements 中添加：\n```yaml\ncom.apple.security.automation.apple-events: true\n```\n\n### Step 6: 构建安装\n\n```bash\nxcodegen generate\nxcodebuild -project APP_NAME.xcodeproj -scheme APP_NAME -configuration Debug build\ncp -R ~/Library/Developer/Xcode/DerivedData/APP_NAME-*/Build/Products/Debug/APP_NAME.app /Applications/\nopen /Applications/APP_NAME.app\npluginkit -e use -i BUNDLE_ID.FinderExtension\n```\n\n### Step 7: 验证\n\n```bash\npluginkit -m -i BUNDLE_ID.FinderExtension\n```\n\n不出现时指引：**系统设置 → 通用 → 登录项与扩展 → 已添加的扩展** → 勾选。\n\n## 沙盒限制与 Helper App 方案\n\nFinder Sync Extension 的沙盒限制非常严格：\n\n| 操作 | 是否允许 | 说明 |\n|------|----------|------|\n| 写入 /tmp | ❌ | 即使添加 temporary-exception 也被阻止 |\n| Process() 子进程 | ❌ | 无法启动外部命令 |\n| NSAppleScript | ❌ | 无法控制其他应用 |\n| NSWorkspace.open(file) | ❌ | 无法打开文件/目录 |\n| NSWorkspace.open(app) | ✅ | 可以打开应用 |\n| NSPasteboard | ✅ | 可以读写剪贴板 |\n\n**推荐方案**：创建一个非沙盒的 Helper App，Extension 把命令放入剪贴板后打开 Helper App，由 Helper App 执行实际操作。\n\n### Helper App 示例\n\n```bash\nmkdir -p \"/Applications/OpenCCHelper.app/Contents/MacOS\"\ncat > \"/Applications/OpenCCHelper.app/Contents/Info.plist\" << 'EOF'\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n    <key>CFBundleExecutable</key><string>run.sh</string>\n    <key>CFBundleIdentifier</key><string>com.lovstudio.OpenCCHelper</string>\n    <key>LSUIElement</key><true/>\n</dict>\n</plist>\nEOF\n\ncat > \"/Applications/OpenCCHelper.app/Contents/MacOS/run.sh\" << 'EOF'\n#!/bin/bash\nCMD=$(pbpaste)\nosascript << APPLESCRIPT\ntell application \"iTerm\"\n    activate\n    tell current window\n        create tab with default profile\n        tell current session\n            write text \"$CMD\"\n        end tell\n    end tell\nend tell\nAPPLESCRIPT\nEOF\nchmod +x \"/Applications/OpenCCHelper.app/Contents/MacOS/run.sh\"\n```\n\nExtension 中调用：\n```swift\nlet command = \"cd '\\(targetPath)' && claude\"\nNSPasteboard.general.clearContents()\nNSPasteboard.general.setString(command, forType: .string)\nNSWorkspace.shared.open(URL(fileURLWithPath: \"/Applications/OpenCCHelper.app\"))\n```\n\n## 其他已知限制\n\n- **Bundle ID 不能含下划线**：使用连字符或驼峰命名（`OpenCC` 而非 `open_cc`）\n- **NSHomeDirectory() 返回容器路径**：沙盒中返回 `~/Library/Containers/<bundle-id>/Data/`，监控目录需硬编码真实路径\n- **NSMenuItem 必须设置 target**：`item.target = self`，否则 action 不会触发\n- Finder Extension 菜单项位置由系统决定，无法排在「新建文件夹」之前\n- Quick Action 环境没有 `$PATH`，工具路径必须用绝对路径\n- Automator workflow 需在 Automator 中打开保存才能注册\n- Extension 使用 ad-hoc 签名，仅限本机使用","tags":["finder","action","skills","lovstudio","agent-skills","ai-coding-assistant","cjk","claude-code","cursor","gemini-cli","markdown-to-docx","markdown-to-pdf"],"capabilities":["skill","source-lovstudio","skill-finder-action","topic-agent-skills","topic-ai-coding-assistant","topic-cjk","topic-claude-code","topic-cursor","topic-gemini-cli","topic-markdown-to-docx","topic-markdown-to-pdf"],"categories":["skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/lovstudio/skills/finder-action","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add lovstudio/skills","source_repo":"https://github.com/lovstudio/skills","install_from":"skills.sh"}},"qualityScore":"0.469","qualityRationale":"deterministic score 0.47 from registry signals: · indexed on github topic:agent-skills · 39 github stars · SKILL.md body (5,441 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-04-21T01:36:43.840Z","embedding":null,"createdAt":"2026-04-18T22:18:58.829Z","updatedAt":"2026-04-21T01:36:43.840Z","lastSeenAt":"2026-04-21T01:36:43.840Z","tsv":"'/applications':362 '/applications/app_name.app':364 '/applications/opencchelper.app':497 '/applications/opencchelper.app/contents/info.plist':436 '/applications/opencchelper.app/contents/macos':434 '/applications/opencchelper.app/contents/macos/run.sh':445,480 '/bin/bash':140,447 '/build/products/debug/app_name.app':361 '/data':511 '/library/containers':510 '/library/developer/xcode/deriveddata/app_name-':360 '/library/services':168,186,202 '/system/library/coreservices/pbs':189 '/tmp':396 '1':111,161,175,219 '2':126,233 '3':135,240 '4':163,282 '5':181,298 '6':194,344 '7':371 'action':4,12,18,49,64,81,102,107,156,318,519,528 'activ':455 'ad':267,540 'ad-hoc':266,539 'api':304 'app':217,246,253,271,353,386,412,419,423,426,429 'app-sandbox':270 'app_name.xcodeproj':351 'appdeleg':290 'appdelegate.swift':236,284 'applescript':324,334,451,476 'applic':453 'applicationdidfinishlaunch':294 'askuserquest':115 'autom':16,65,108,165,195,201,206,532,535 'automat':35 'b':23,212 'background':98 'base':38 'bash':128,139,183,198,221,346,373,431 'blank':31,96 'blank-spac':30 'brew':132,229 'build':357 'bundl':249,499 'bundle_id.finderextension':369,377 'cat':435,444 'cc':506 'cd':486 'cfbundleexecut':438 'cfbundleidentifi':440 'chmod':478 'class':289 'claud':488 'click':10 'cmd':208,448,469 'cocoa':287 'code':263 'com.apple.automator.filesystemobject':177 'com.apple.automator.servicesmenu':179 'com.apple.findersync':261 'com.apple.security.automation.apple':340 'com.lovstudio.opencchelper':441 'command':485,491 'configur':355 'context':21,33 'contextualmenuforcontain':312 'continu':146 'cp':358 'creat':459 'current':457,465 'debug':356 'default':462 'directoryurl':306 'dock':257 'done':153 'e':366 'end':470,472,474 'entitl':337 'eof':437,443,446,477 'event':341 'except':400 'extens':26,70,85,89,215,259,390,420,481,522,537 'f':142,145,148,150 'fifindersynccontroller.default':305,314 'file':408 'file/folder':20 'filemanager.default.createfile':321 'files.user-selected.read':278 'fileurlwithpath':308,496 'finder':3,7,24,48,51,54,69,84,88,192,213,388,521 'finder-act':2,47 'finderextens':237 'findersync.swift':238,300 'finder右键':46 'fortyp':492 'func':293 'generat':5,348 'helper':385,418,422,425,428 'hoc':268,541 'id':250,500 'ident':265 'import':286 'inputmethod':160,174 'instal':133,230 'intent':41 'item.target':516 'iterm':454 'iterm2/terminal':326 'jpg':121 'killal':191 'let':484 'lint':185 'lovstudio':1 'lsuielement':254,442 'm':375 'mac':6,50 'main':288 'md':120 'mention':45 'menu':11,310 'menus':22,34 'mkdir':432 'mode':14,37,104,211 'name':247,354 'notif':295,296 'nsapplescript':405 'nsapplicationdeleg':292 'nsextensionpointidentifi':260 'nshomedirectori':507 'nsmenuitem':513 'nsobject':291 'nspasteboard':414 'nspasteboard.general.clearcontents':489 'nspasteboard.general.setstring':490 'nsworkspace.open':407,411 'nsworkspace.shared.open':494 'o':151 'open':199,363,505 'opencc':503 'osascript':450 'output':147,152 'p':433 'path':158,530 'pbpast':449 'pdf':78,119 'pdf2png':77 'pluginkit':365,374 'plutil':184 'process':330,402 'profil':463 'project':350 'project.yml':235,242 'quick':17,63,80,101,106,155,527 'r':359 'references/applescript-iterm.swift':328 'references/automator-template.xml':172 'references/finder-sync-template.swift':302 'references/xcodegen-template.yml':244 'right':9 'right-click':8 'run.sh':439 'sandbox':272 'scheme':352 'select':36 'self':517 'serviceinputtypeidentifi':176 'session':466 'shell':137,332 'sign':264 'skill' 'skill-finder-action' 'source-lovstudio' 'space':32,97 'step':110,125,134,162,180,193,218,232,239,281,297,343,370 'string':493 'swift':27,71,216,285,483 'sync':25,214,389 'tab':460 'target':515 'targetedurl':315 'targetpath':487 'tell':452,456,464,471,473,475 'temporari':399 'temporary-except':398 'temporary-exception.files.absolute-path.read':276 'text':468 'topic-agent-skills' 'topic-ai-coding-assistant' 'topic-cjk' 'topic-claude-code' 'topic-cursor' 'topic-gemini-cli' 'topic-markdown-to-docx' 'topic-markdown-to-pdf' 'trigger':42 'true':255,273,342 'two':13 'updat':190 'url':307,495 'use':367 'user':40,44 'window':458 'workflow':66,109,166,204,533 'workflow/contents/document.wflow':170,188 'workflowtypeidentifi':178 'write':277,279,467 'x':479 'xcodebuild':225,349 'xcodegen':28,72,223,227,231,347 'yaml':339 '不会触发':520 '不出现时指引':378 '不存在则提示':131 '不能含下划线':501 '中':207 '中打开保存才能注册':536 '中添加':338 '中调用':482 '之前':526 '也被阻止':401 '仅限本机使用':543 '使用':538 '使用连字符或驼峰命名':502 '保存':196 '保存一次才会正式注册':210 '关键':197 '关键点':251 '关键词命中':87 '关键配置':173 '其他已知限制':498 '其他情况':100 '具体命令':149 '写入':395 '分析需求':112 '则':228 '创建':164,167 '创建一个非沙盒的':417 '创建文件':320 '创建项目结构':234 '动作不需要选中文件即可触发':99 '动作名称':74,116,169,187,203 '勾选':383 '即使添加':397 '参数格式':73 '可以打开应用':413 '可以读写剪贴板':415 '右键':60,67 '右键菜单动作':55 '右键菜单动作生成器':52 '右键菜单显示名':117 '否则':518 '否则扩展不会被系统加载':274 '启动':331 '命令':333 '和':248 '图标':258 '在空白处右键创建markdown文件':83 '场景':57 '子进程':403 '宿主':252 '将pdf所有页面纵向拼接成一张png':79 '工具路径必须用绝对路径':531 '工具路径用绝对路径':154 '已添加的扩展':382 '常见':317 '必须在':205 '必须设置':514 '所需工具':130 '打开终端':323 '执行实际操作':427 '执行脚本':329 '技术方案':59 '把命令放入剪贴板后打开':421 '接收文件参数':159 '控制':325 '推荐方案':416 '提到':91 '操作':392 '收集':113 '文件':61 '文件写入需用':275 '文件夹':62 '新建md文件':82 '新建文件':95 '新建文件夹':525 '方案':387 '无':256 '无效':280 '无法启动外部命令':404 '无法打开文件':409 '无法排在':524 '无法控制其他应用':406 '是否允许':393 '替换':245 '构建安装':345 '核心':303 '核心命令':123 '根据用户描述生成':53 '检查依赖':127 '检查工具链':220 '模式':58,90,103,319 '模式判断':86 '模板见':171,243,301 '沙盒中返回':509 '沙盒必须开启':269 '沙盒限制与':384 '环境没有':157,529 '生成':136,241,283,299 '用什么工具做什么':124 '由':424 '登录项与扩展':381 '的沙盒限制非常严格':391 '监控所有目录':309 '监控目录需硬编码真实路径':512 '目录':410 '目录背景':94 '示例':76,430 '空白处':68,92 '空白处右键菜单':313 '等':122 '签名':262,542 '系统设置':379 '缺':226 '缺失时':114 '而非':504 '背景':93 '脚本':138 '自动判断模式':56 '自动化':335 '自动递增文件名':322 '获取当前目录':316 '菜单项位置由系统决定':523 '见':327 '触发描述':75 '触发文件类型':118 '说明':394 '返回容器路径':508 '通用':380 '需在':534 '需要在':336 '验证':372 '验证注册':182","prices":[{"id":"a92c5c9f-3129-40d4-a8f3-c0d56e60f58b","listingId":"8ecd100f-baa9-4d0b-963e-02e5ea102721","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"lovstudio","category":"skills","install_from":"skills.sh"},"createdAt":"2026-04-18T22:18:58.829Z"}],"sources":[{"listingId":"8ecd100f-baa9-4d0b-963e-02e5ea102721","source":"github","sourceId":"lovstudio/skills/finder-action","sourceUrl":"https://github.com/lovstudio/skills/tree/main/skills/finder-action","isPrimary":false,"firstSeenAt":"2026-04-18T22:18:58.829Z","lastSeenAt":"2026-04-21T01:36:43.840Z"}],"details":{"listingId":"8ecd100f-baa9-4d0b-963e-02e5ea102721","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"lovstudio","slug":"finder-action","github":{"repo":"lovstudio/skills","stars":39,"topics":["agent-skills","ai-coding-assistant","cjk","claude-code","cursor","gemini-cli","markdown-to-docx","markdown-to-pdf"],"license":"mit","html_url":"https://github.com/lovstudio/skills","pushed_at":"2026-04-20T21:40:57Z","description":"Agent skills for AI coding assistants — Markdown to PDF/DOCX with 14 themes, CJK support","skill_md_sha":"29a0e38575f5f821b8f895521d36383c97114b17","skill_md_path":"skills/finder-action/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/lovstudio/skills/tree/main/skills/finder-action"},"layout":"multi","source":"github","category":"skills","frontmatter":{"name":"lovstudio:finder-action","license":"MIT","description":"Generate Mac Finder right-click menu actions. Two modes: (A) Automator Quick Action for file/folder context menus, (B) Finder Sync Extension (Swift + xcodegen) for blank-space context menus. Automatically selects mode based on user intent. Trigger when user mentions \"Finder右键\", \"右键菜单\", \"Quick Action\", \"Finder extension\", \"空白处右键\", \"新建文件\", \"context menu\", or wants to add custom actions to Finder.","compatibility":"macOS 14+. Mode A requires Automator. Mode B requires Xcode and xcodegen (`brew install xcodegen`). Ad-hoc signed — works locally, not distributable."},"skills_sh_url":"https://skills.sh/lovstudio/skills/finder-action"},"updatedAt":"2026-04-21T01:36:43.840Z"}}