Generate Finder context menu actions based on the user's description, automatically determining the mode:
| Scenario | Mode | Technical solution |
|---|---|---|
| Right-clickfile/folder | Quick Action | Automator workflow |
| Right-clickblank space | Finder Extension | Swift + xcodegen |
Parameter format
<动作名称> [触发描述]
Example:
pdf2png .pdf 将PDF所有页面纵向拼接成一张PNG→ Quick Action新建md文件 在空白处右键创建markdown文件→ Finder Extension
Mode determination
Keyword hit → Finder Extension mode:
- Mentions "blank space", "background", "directory background", "new file", "blank space", "background"
- The action can be triggered without selecting a file
Other cases → Quick Action mode
Mode A: Quick Action(Automator workflow)
Step 1: Analyze requirements
Collect (use AskUserQuestion when missing):
- Action name: display name in the context menu
- Trigger file types:
.pdf、.md、.jpg, etc. - Core command: what to do with which tool
Step 2: Check dependencies
which <所需工具>
If it does not exist, prompt brew install <tool>。
Step 3: Generate shell script
#!/bin/bash
for f in "$@"; do
[[ "$f" == *.<ext> ]] || continue
output="${f%.<ext>}.<out_ext>"
<具体命令> "$f" -o "$output"
done
- Use absolute paths for tool paths (the Quick Action environment does not have
$PATH) "$@"receive file parameters (inputMethod=1)
Step 4: Create Automator workflow
Create ~/Library/Services/<动作名称>.workflow/Contents/document.wflow。
See template at references/automator-template.xml。
Key configuration:
inputMethod:1serviceInputTypeIdentifier:com.apple.Automator.fileSystemObjectworkflowTypeIdentifier:com.apple.Automator.servicesMenu
Step 5: Verify registration
plutil -lint ~/Library/Services/<动作名称>.workflow/Contents/document.wflow
/System/Library/CoreServices/pbs -update
killall Finder
Step 6: Automator save (critical)
open -a Automator ~/Library/Services/<动作名称>.workflow
It must be saved once with Cmd+S in Automator before it is officially registered.
Mode B: Finder Sync Extension(Swift app)
Step 1: Check toolchain
which xcodegen && which xcodebuild
If xcodegen is missing, brew install xcodegen。
Step 2: Create project structure
<ProjectName>/
├── project.yml
├── <ProjectName>/
│ └── AppDelegate.swift
└── FinderExtension/
└── FinderSync.swift
Step 3: Generate project.yml
See template at references/xcodegen-template.yml. Replace APP_NAME and BUNDLE_ID。
Key points:
- Host App:
LSUIElement: true(no Dock icon) - Extension:
NSExtensionPointIdentifier: com.apple.FinderSync - Signing:
CODE_SIGN_IDENTITY: "-"(ad-hoc) - Sandbox must be enabled(
app-sandbox: true), otherwise the extension will not be loaded by the system - File writes must use
temporary-exception.files.absolute-path.read-write: [/],files.user-selected.read-writeis invalid
Step 4: Generate AppDelegate.swift
import Cocoa
@main
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ notification: Notification) {}
}
Step 5: Generate FinderSync.swift
See template at references/finder-sync-template.swift。
Core APIs:
FIFinderSyncController.default().directoryURLs = [URL(fileURLWithPath: "/")]— monitor all directoriesmenu(for: .contextualMenuForContainer)— blank-space context menuFIFinderSyncController.default().targetedURL()— get the current directory
Common action patterns:
- Create file:
FileManager.default.createFile+ auto-incrementing filename - Open terminal: AppleScript controls iTerm2/Terminal (see
references/applescript-iterm.swift) - Execute script:
Process()launch shell command
AppleScript automationneeds to be added in entitlements:
com.apple.security.automation.apple-events: true
Step 6: Build and install
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: Verify
pluginkit -m -i BUNDLE_ID.FinderExtension
If it does not appear, guide:System Settings → General → Login Items & Extensions → added extensions → check it.
Sandbox limitations and Helper App solution
The sandbox restrictions of Finder Sync Extension are very strict:
| Operation | Allowed | Description |
|---|---|---|
| Write to /tmp | ❌ | Even adding temporary-exception is blocked |
| Process() child process | ❌ | Unable to launch external commands |
| NSAppleScript | ❌ | Unable to control other apps |
| NSWorkspace.open(file) | ❌ | Unable to open files/directories |
| NSWorkspace.open(app) | ✅ | Can open apps |
| NSPasteboard | ✅ | Can read and write the clipboard |
Recommended solution: Create a non-sandboxed Helper App. The Extension puts the command on the clipboard and then opens the Helper App, which performs the actual operation.
Helper App example
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"
Calling from the Extension:
let command = "cd '\(targetPath)' && claude"
NSPasteboard.general.clearContents()
NSPasteboard.general.setString(command, forType: .string)
NSWorkspace.shared.open(URL(fileURLWithPath: "/Applications/OpenCCHelper.app"))
Other known limitations
- Bundle ID cannot contain underscores: Use hyphens or camel case (
OpenCCinstead ofopen_cc) - NSHomeDirectory() returns the container path: In the sandbox it returns
~/Library/Containers/<bundle-id>/Data/, and monitored directories must hard-code the real path - NSMenuItem must have target set:
item.target = self, otherwise the action will not be triggered - The position of the Finder Extension menu item is determined by the system and cannot be placed before "New Folder"
- The Quick Action environment does not have
$PATH, and tool paths must use absolute paths - The Automator workflow must be opened and saved in Automator to be registered
- The Extension uses ad-hoc signing and is limited to use on this machine