Automates macOS applications and system-level tasks using AppleScript and JavaScript for Automation.

Install

mkdir -p .claude/skills/applescript && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/275" && unzip -o skill.zip -d .claude/skills/applescript && rm skill.zip

Installs to .claude/skills/applescript

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Expert in AppleScript and JavaScript for Automation (JXA) for macOS system scripting. Specializes in secure script execution, application automation, and system integration. HIGH-RISK skill due to shell command execution and system-wide control capabilities.
258 charsno explicit “when” triggerlonger than Claude Code's old 250-char listing cap (fine on current versions)
Advanced

Key capabilities

  • Validate and sanitize shell script inputs
  • Execute JXA and AppleScript automations
  • Apply execution time limits
  • Manage secure application interaction

How it works

Enforces a security-first wrapper around osascript, filtering out dangerous commands and ensuring all inputs are sanitized before execution.

Inputs & outputs

You give it
Task description and target app name
You get back
Executed script feedback

When to use applescript

  • Automate macOS application interactions
  • Script system-wide tasks
  • Batch process file operations
  • Execute JXA scripts for workflows

About this skill

1. Overview

Risk Level: HIGH - Shell command execution, application control, file system access

You are an expert in AppleScript automation with deep expertise in:

  • AppleScript Language: Script composition, application scripting dictionaries
  • JavaScript for Automation (JXA): Modern alternative with JavaScript syntax
  • osascript Execution: Command-line script execution and security
  • Sandboxing Considerations: App sandbox restrictions and automation permissions

Core Expertise Areas

  1. Script Composition: Secure AppleScript/JXA patterns
  2. Application Automation: Scriptable app interaction
  3. Security Controls: Input sanitization, command filtering
  4. Process Management: Safe execution with timeouts

2. Core Responsibilities

2.1 Core Principles

When creating or executing AppleScripts:

  • TDD First - Write tests before implementing AppleScript automation
  • Performance Aware - Cache scripts, batch operations, minimize app activations
  • Sanitize all inputs before script interpolation
  • Block dangerous commands (rm, sudo, curl piped to sh)
  • Validate target applications against blocklist
  • Enforce execution timeouts
  • Log all script executions

2.2 Security-First Approach

Every script execution MUST:

  1. Sanitize user-provided inputs
  2. Check for dangerous patterns
  3. Validate target applications
  4. Execute with timeout limits
  5. Log execution details

2.3 Blocked Operations

Never allow scripts that:

  • Execute arbitrary shell commands without validation
  • Access password managers or security tools
  • Modify system files or preferences
  • Download and execute code
  • Access financial applications

3. Technical Foundation

3.1 Execution Methods

Command Line: osascript

osascript -e 'tell application "Finder" to activate'
osascript script.scpt
osascript -l JavaScript -e 'Application("Finder").activate()'

Python Integration: subprocess or py-applescript

import subprocess
result = subprocess.run(['osascript', '-e', script], capture_output=True)

3.2 Key Security Considerations

Risk AreaMitigationPriority
Command injectionInput sanitizationCRITICAL
Shell escapeUse quoted form ofCRITICAL
Privilege escalationBlock do shell script with adminHIGH
Data exfiltrationBlock network commandsHIGH

4. Implementation Patterns

Pattern 1: Secure Script Execution

import subprocess, re, logging

class SecureAppleScriptRunner:
    BLOCKED_PATTERNS = [
        r'do shell script.*with administrator',
        r'do shell script.*sudo',
        r'do shell script.*(rm -rf|rm -r)',
        r'do shell script.*curl.*\|.*sh',
        r'keystroke.*password',
    ]
    BLOCKED_APPS = ['Keychain Access', '1Password', 'Terminal', 'System Preferences']

    def __init__(self, permission_tier: str = 'standard'):
        self.permission_tier = permission_tier
        self.logger = logging.getLogger('applescript.security')

    def execute(self, script: str, timeout: int = 30) -> tuple[str, str]:
        self._check_blocked_patterns(script)
        self._check_blocked_apps(script)
        self.logger.info(f'applescript.execute', extra={'script': script[:100]})
        try:
            result = subprocess.run(['osascript', '-e', script],
                capture_output=True, text=True, timeout=timeout)
            return result.stdout.strip(), result.stderr.strip()
        except subprocess.TimeoutExpired:
            raise TimeoutError(f"Script timed out after {timeout}s")

    def _check_blocked_patterns(self, script: str):
        for pattern in self.BLOCKED_PATTERNS:
            if re.search(pattern, script, re.IGNORECASE):
                raise SecurityError(f"Blocked pattern: {pattern}")

    def _check_blocked_apps(self, script: str):
        for app in self.BLOCKED_APPS:
            if app.lower() in script.lower():
                raise SecurityError(f"Access to {app} blocked")

Pattern 2: Safe Input Interpolation

class SafeScriptBuilder:
    """Build AppleScript with safe input interpolation."""

    @staticmethod
    def escape_string(value: str) -> str:
        """Escape string for AppleScript interpolation."""
        # Escape backslashes and quotes
        escaped = value.replace('\\', '\\\\').replace('"', '\\"')
        return escaped

    @staticmethod
    def quote_for_shell(value: str) -> str:
        """Quote value for shell command within AppleScript."""
        # Use AppleScript's quoted form of
        return f'quoted form of "{SafeScriptBuilder.escape_string(value)}"'

    def build_tell_script(self, app_name: str, commands: list[str]) -> str:
        """Build safe tell application script."""
        # Validate app name
        if not re.match(r'^[a-zA-Z0-9 ]+$', app_name):
            raise ValueError("Invalid application name")

        escaped_app = self.escape_string(app_name)
        escaped_commands = [self.escape_string(cmd) for cmd in commands]

        script = f'''
tell application "{escaped_app}"
    {chr(10).join(escaped_commands)}
end tell
'''
        return script.strip()

    def build_safe_shell_command(self, command: str, args: list[str]) -> str:
        """Build safe do shell script command."""
        # Allowlist of safe commands
        SAFE_COMMANDS = ['ls', 'pwd', 'date', 'whoami', 'echo']

        if command not in SAFE_COMMANDS:
            raise SecurityError(f"Command {command} not in allowlist")

        # Quote all arguments
        quoted_args = ' '.join(f'"{self.escape_string(arg)}"' for arg in args)

        return f'do shell script "{command} {quoted_args}"'

Pattern 3: JXA (JavaScript for Automation)

class SecureJXARunner {
    constructor() {
        this.blockedApps = ['Keychain Access', 'Terminal', 'System Preferences'];
    }

    runApplication(appName, action) {
        if (this.blockedApps.includes(appName)) {
            throw new Error(`Access to ${appName} is blocked`);
        }
        return Application(appName)[action]();
    }

    safeShellScript(command) {
        const blocked = [/rm\s+-rf/, /sudo/, /curl.*\|.*sh/];
        for (const p of blocked) {
            if (p.test(command)) throw new Error('Blocked command');
        }
        const app = Application.currentApplication();
        app.includeStandardAdditions = true;
        return app.doShellScript(command);
    }
}

Pattern 4: Application Dictionary Validation

class AppDictionaryValidator:
    def get_app_dictionary(self, app_name: str) -> str:
        result = subprocess.run(['sdef', f'/Applications/{app_name}.app'],
            capture_output=True, text=True)
        return result.stdout

    def is_scriptable(self, app_name: str) -> bool:
        try:
            return bool(self.get_app_dictionary(app_name).strip())
        except Exception:
            return False

5. Implementation Workflow (TDD)

Step 1: Write Failing Test First

import pytest

class TestSecureAppleScriptRunner:
    def test_simple_script_execution(self):
        runner = SecureAppleScriptRunner()
        stdout, stderr = runner.execute('return "hello"')
        assert stdout == "hello"

    def test_blocked_pattern_raises_error(self):
        runner = SecureAppleScriptRunner()
        with pytest.raises(SecurityError):
            runner.execute('do shell script "rm -rf /"')

    def test_blocked_app_raises_error(self):
        runner = SecureAppleScriptRunner()
        with pytest.raises(SecurityError):
            runner.execute('tell application "Keychain Access" to activate')

    def test_timeout_enforcement(self):
        runner = SecureAppleScriptRunner()
        with pytest.raises(TimeoutError):
            runner.execute('delay 10', timeout=1)

Step 2: Implement Minimum to Pass

class SecureAppleScriptRunner:
    def execute(self, script: str, timeout: int = 30):
        self._check_blocked_patterns(script)
        self._check_blocked_apps(script)
        result = subprocess.run(['osascript', '-e', script],
            capture_output=True, text=True, timeout=timeout)
        return result.stdout.strip(), result.stderr.strip()

Step 3: Refactor and Verify

pytest tests/test_applescript.py -v
pytest tests/test_applescript.py -k "blocked or security" -v

6. Performance Patterns

Pattern 1: Script Caching

# BAD: Recompile script every execution
result = subprocess.run(['osascript', '-e', script], capture_output=True)

# GOOD: Cache compiled scripts
class CachedScriptRunner:
    _cache = {}
    def execute_cached(self, script_id: str, script: str):
        if script_id not in self._cache:
            import tempfile
            _, path = tempfile.mkstemp(suffix='.scpt')
            subprocess.run(['osacompile', '-o', path, '-e', script])
            self._cache[script_id] = path
        return subprocess.run(['osascript', self._cache[script_id]], capture_output=True)

Pattern 2: Batch Operations

# BAD: Multiple separate script calls
subprocess.run(['osascript', '-e', f'tell app "{app}" to set bounds...'])
subprocess.run(['osascript', '-e', f'tell app "{app}" to activate'])

# GOOD: Single batched script
script = f'''tell application "{app}"
    set bounds of window 1 to {{{x}, {y}, {w}, {h}}}
    activate
end tell'''
subprocess.run(['osascript', '-e', script], capture_output=True)

Pattern 3: Async Execution

# BAD: Blocking execution
result = subprocess.run(['osascript', '-e', script], capture_output=True)

# GOOD: Async execution
async def run_script_async(script: str, timeout: int = 30):
    proc = await asyncio.create_subprocess_exec('osascript', '-e', script,
        stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE)
    stdout, stderr = await asyncio.wait_for(proc.communicate(), t

---

*Content truncated.*

When not to use it

  • Executing unvalidated external shell commands
  • Interacting with banking or password applications

Prerequisites

MacOSOsascript enabled environment

Limitations

  • System-level restrictions (SIP)
  • Requires macOS specific knowledge
  • Strict blocklist for high-risk operations

How it compares

It includes an active security layer that blocks harmful commands like sudo or rm, which standard execution methods lack.

Compared to similar skills

applescript side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
applescript (this skill)288moReviewAdvanced
bazel-build-optimization142moNo flagsAdvanced
home-assistant-manager98moReviewAdvanced
swarm-advanced74moReviewAdvanced

Try saying

Example prompts that trigger this skill in your AI assistant.

Search skills

Search the agent skills registry