From macos-automation
This skill should be used when the user asks to "automate macOS", "use AppleScript", "write JXA", "automate Mac apps", "script Apple applications", "use osascript", "run JavaScript for Automation", or needs guidance on macOS automation fundamentals, TypeScript/Bun integration patterns, or choosing between AppleScript and JXA.
How this skill is triggered — by the user, by Claude, or both
Slash command
/macos-automation:macos-automation-coreThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Foundation skill for automating Apple's native macOS applications using AppleScript and JXA (JavaScript for Automation).
Foundation skill for automating Apple's native macOS applications using AppleScript and JXA (JavaScript for Automation).
macOS provides two powerful automation languages for controlling native applications:
Both provide full access to application scripting dictionaries, enabling programmatic control of Mail, Calendar, Notes, Reminders, Finder, Safari, and more.
Use this knowledge when:
| Feature | JXA | AppleScript |
|---|---|---|
| Syntax | Modern JavaScript | Natural language-like |
| Output format | JSON (easy to parse) | Plain text |
| TypeScript integration | Excellent | Requires parsing |
| Learning curve | Low (if familiar with JS) | Medium |
| Documentation | Community-driven | Extensive Apple docs |
| Recommendation | ✅ Preferred | Use when needed |
When to choose JXA:
When to choose AppleScript:
The recommended pattern for executing JXA from TypeScript:
async function runJXA<T>(script: string): Promise<T> {
const proc = Bun.spawn(['osascript', '-l', 'JavaScript', '-e', script], {
stdout: 'pipe',
stderr: 'pipe',
})
const output = await new Response(proc.stdout).text()
const error = await new Response(proc.stderr).text()
if (error && !error.includes('Warning')) {
throw new Error(`JXA execution failed: ${error}`)
}
if (!output.trim()) {
return [] as T
}
try {
return JSON.parse(output)
} catch (e) {
throw new Error(`Failed to parse JXA output: ${output}`)
}
}
Key benefits:
<T>Execute JXA from command line:
osascript -l JavaScript -e 'Application("Mail").inbox().unreadCount()'
Execute JXA file:
osascript -l JavaScript /path/to/script.js
Execute AppleScript:
osascript -e 'tell application "Mail" to count of (messages of inbox)'
Execute AppleScript file:
osascript /path/to/script.applescript
Basic JXA script pattern:
#!/usr/bin/osascript -l JavaScript
function run() {
const App = Application('Mail') // Target application
App.includeStandardAdditions = true // Enable system events
// Your automation logic here
const unreadCount = App.inbox().unreadCount()
// Return JSON for easy parsing
return JSON.stringify({ unread: unreadCount })
}
Important conventions:
function run() as entry pointincludeStandardAdditions for system accessJSON.stringify() for structured outputAccess applications by name:
const Mail = Application('Mail')
const Calendar = Application('Calendar')
const Notes = Application('Notes')
const System = Application('System Events')
Check if application is running:
const System = Application('System Events')
const isRunning = System.processes.byName('Mail').exists()
Common error patterns:
try {
const result = await runJXA<T>(script)
return result
} catch (error) {
if (error instanceof Error && error.message.includes("isn't running")) {
throw new Error('Application is not running. Please open it first.')
}
if (error.message.includes('permission')) {
throw new Error('Automation permission denied. Check System Settings.')
}
throw error
}
Common errors:
"Application isn't running" - Target app needs to be launched"Not authorized" / "permission denied" - Automation permissions not granted"Can't get..." - Property doesn't exist or wrong object type| Task | JXA Pattern |
|---|---|
| Get application | Application('AppName') |
| Check if running | Application('System Events').processes.byName('App').exists() |
| Get property | obj.propertyName() |
| Call method | obj.methodName(arg) |
| Create object | App.ObjectType({ prop: value }) |
| Get collection | App.objects() (returns array) |
| Filter collection | objects.filter(predicate) |
| Access by name | objects.byName('name') |
| Command | Purpose |
|---|---|
osascript -l JavaScript -e 'code' | Execute inline JXA |
osascript -l JavaScript script.js | Execute JXA file |
osascript -e 'code' | Execute inline AppleScript |
osascript script.applescript | Execute AppleScript file |
| Pattern | Use Case |
|---|---|
runJXA<T>(script) | Type-safe execution with JSON parsing |
Bun.spawn(['osascript', ...]) | Execute scripts with Bun runtime |
JSON.stringify(result) | Return structured data from JXA |
| Generic types | Ensure type safety in wrappers |
macOS requires explicit permission for apps to control other apps.
Grant permissions:
Check permissions programmatically:
// This will trigger permission dialog if not granted
const Mail = Application('Mail')
const inbox = Mail.inbox() // Triggers permission prompt
Permission denied error:
Error: Not authorized to send Apple events to Mail.
Solution: Grant automation permission in System Settings.
For detailed troubleshooting, see references/permissions-guide.md.
When embedding user data in scripts, escape special characters:
function escapeJXA(str: string): string {
return str
.replace(/\\/g, '\\\\')
.replace(/"/g, '\\"')
.replace(/\n/g, '\\n')
.replace(/\r/g, '\\r')
}
const subject = escapeJXA(userInput)
const script = `App.create({ subject: "${subject}" })`
Use inline scripts (via -e) for:
Use script files for:
Enable verbose output:
osascript -l JavaScript -s s script.js # Print statements
Print to console:
console.log('Debug:', variable) // Shows in stderr
Test accessibility:
# Test if app is accessible
osascript -l JavaScript -e 'Application("Mail").version()'
DO: Access only needed properties
// Good - specific properties
const msg = messages[0]
const subject = msg.subject()
const sender = msg.sender()
DON'T: Access all properties
// Slow - gets everything
const allProps = msg.properties()
Batch operations efficiently:
Load app-specific skills for detailed automation patterns:
Guides creation, editing, and verification of skills for AI coding agents using test-driven development with subagent scenarios. Use when authoring or debugging skills.
npx claudepluginhub mattwag05/mw-plugins --plugin macos-automation