Opinionated rules and patterns for creating reliable, maintainable macOS automations with Shortcuts, Automator, AppleScript and JavaScript for Automation (JXA).
You're burning hours every week on repetitive Mac tasks that should take seconds. File organization, app switching, data processing, system maintenance—all perfect automation candidates that most developers either avoid (thinking it's too complex) or implement poorly (creating brittle scripts that break constantly).
Manual Mac workflows kill developer productivity through death by a thousand paper cuts:
Most automation attempts fail because they're either too brittle (break when apps update) or too complex (unmaintainable shell script monsters). You need automation that works reliably and stays working.
These rules implement a layered automation strategy that prioritizes reliability and maintainability:
Tool Selection Hierarchy: Start with Shortcuts (built-in, future-proof) → Automator (visual workflows) → JXA/AppleScript (custom logic) → third-party tools only when necessary.
Atomic Composition: Build small, single-purpose actions that chain together. A "process-screenshots" workflow combines "resize-images" + "add-timestamp" + "move-to-archive" actions.
Idempotent Design: Every automation runs safely multiple times. No corrupted state, no duplicate files, no broken workflows.
// JXA example: Idempotent file processing
#!/usr/bin/osascript -l JavaScript
const app = Application.currentApplication();
app.includeStandardAdditions = true;
function processScreenshots() {
const finder = Application('Finder');
const desktop = finder.desktop.files().filter(f =>
f.name().endsWith('.png') && !f.name().includes('_processed')
);
desktop.forEach(file => {
try {
const newName = `${file.name().slice(0, -4)}_processed.png`;
// Idempotent: only process if not already processed
if (!finder.desktop.files.byName(newName).exists()) {
file.name = newName;
}
} catch (err) {
throw new Error(`process screenshot ${file.name()} ➜ ${err}`);
}
});
}
Time Multiplication: Transform 5-minute manual tasks into 5-second automations. A developer processing 20 screenshots daily saves 90 minutes weekly.
Mental Load Reduction: Eliminate decision fatigue from repetitive choices. Your automation handles the "where does this file go?" question consistently.
Error Elimination: No more misnamed files, forgotten steps, or inconsistent processing. Automation executes the same way every time.
Scalability: Handle batch operations effortlessly. Process 100 files as easily as 1 file.
Replace manual file organization with intelligent processing:
// Screenshots auto-organize by content and timestamp
function organizeScreenshots() {
const screenshots = getDesktopScreenshots();
screenshots.forEach(screenshot => {
const timestamp = new Date().toISOString().slice(0, 10);
const category = detectScreenshotType(screenshot); // 'code', 'design', 'error'
const destination = `~/Documents/Screenshots/${category}/${timestamp}/`;
ensureDirectoryExists(destination);
moveFile(screenshot, destination);
});
}
Before: Manually drag each screenshot to appropriate folder, rename with date After: Screenshots automatically categorized and archived on save
Replace Alt-Tab hunting with intelligent app management:
# Keyboard Maestro macro: 🌐smart-switch-context
on run
tell application "System Events"
set frontApp to name of first application process whose frontmost is true
if frontApp is "Xcode" then
activate application "Simulator"
else if frontApp is "Visual Studio Code" then
activate application "Terminal"
else if frontApp is "Figma" then
activate application "Finder"
end if
end tell
end run
Before: Hunt through 15 open apps to find the related tool After: Context-aware switching brings up the right companion app
Replace manual project initialization with environment automation:
# project-setup.sh (called from Shortcuts)
#!/bin/zsh
PROJECT_NAME="$1"
PROJECT_TYPE="$2"
# Create directory structure
mkdir -p ~/Development/$PROJECT_NAME/{src,tests,docs,scripts}
# Initialize git and add templates
cd ~/Development/$PROJECT_NAME
git init
cp ~/Automation/templates/$PROJECT_TYPE/.* .
# Open in preferred editor
code ~/Development/$PROJECT_NAME
Before: 15 minutes of manual folder creation, file copying, and tool launching After: Complete development environment ready in 30 seconds
# Create automation directory structure
mkdir -p ~/Automation/{shortcuts,automator,jxa,logs}
mkdir -p ~/Automation/lib
mkdir -p ~/Automation/test_data
# Initialize version control
cd ~/Automation && git init
Start with a simple, high-impact workflow:
Choose Your Pain Point: Pick the most frequent manual task (screenshot organization, file processing, app launching)
Build Atomic Actions: Create the smallest useful automation first
// Start simple: just move screenshots to dated folder
const today = new Date().toISOString().slice(0, 10);
const targetDir = `~/Documents/Screenshots/${today}/`;
Test Thoroughly: Run with 2-3 files before automating your entire workflow
Build on your foundation:
Immediate Gains (Week 1):
Compound Benefits (Month 1):
Long-term Transformation (Month 3):
Measurable Productivity Metrics:
Your Mac becomes a productivity multiplier instead of a collection of manual tools. Every repetitive task becomes an automation opportunity, and every automation compounds your efficiency gains.
The difference between manual Mac users and automation-powered developers isn't just time—it's the mental freedom to focus on high-value work instead of repetitive digital housekeeping.
You are an expert in macOS automation tooling: Shortcuts, Automator, JavaScript for Automation (JXA), AppleScript, shell scripting, Keyboard Maestro, BetterTouchTool, Raycast.
Key Principles
- Prefer the simplest built-in tool that satisfies the requirement (Shortcuts → Automator → Script → 3rd-party macro).
- Tackle one repetitive pain-point at a time; deliver fast, testable wins.
- Build atomic actions; compose larger workflows by chaining small, single-responsibility steps.
- All scripts and shortcuts must be idempotent; running twice should never corrupt state.
- Name every workflow with an imperative verb + object (e.g. “resize-images”, “archive-downloads”).
- Store all automations under ~/Automation/<tool>/<category>, version-controlled with git.
- Comment every non-trivial step with why, not what.
JavaScript for Automation (JXA)
- Always begin with: `#!/usr/bin/osascript -l JavaScript` and make files executable (chmod +x).
- Use strict mode and const/let; never use var.
- Prefer Application objects over `app = Application.currentApplication()` for clarity.
- Wrap Finder, System Events, or 3rd-party app calls in helper functions that return plain data, never raw ObjC bridged objects.
- Use template literals for AppleScript-style `doShellScript` strings; escape double quotes explicitly.
- Catch automation errors with try/catch blocks that re-throw an Error containing the failing step, e.g.:
```js
try {
safari.activate();
} catch (err) {
throw new Error(`activate Safari ➜ ${err}`);
}
```
- Export reusable functions in common.js and import via `ObjC.import("~/Automation/lib/common.js")`.
- Keep each script ≤ 150 LOC; if larger, split into modules and orchestrate via Automator/Shortcuts.
AppleScript (where unavoidable)
- Indent 2 spaces. Avoid tabs.
- Always declare `on run {input, parameters}` handlers for Automator compatibility.
- Convert AppleScript dates to ISO‐8601 strings before handing off to shell/JXA.
Shell Glue
- Use zsh for all glued shell actions.
- Never parse human text; output JSON and consume in JXA with `JSON.parse`.
- Prefix destructive shell commands with a dry-run flag that can be toggled via environment variable (`DRY_RUN=1`).
Error Handling and Validation
- Validate prerequisites at the start: application installed, files exist, network up.
- Fail fast: if a validation fails, abort the entire workflow and display a macOS notification using `display notification "..."`.
- Use early returns inside Shortcuts conditional blocks to prevent branching explosion.
- Send a unified error report: timestamp, script name, step, stack trace → save to ~/Automation/logs/YYYY-MM-DD.log.
Framework-Specific Rules
Shortcuts
- Use folders to group shortcuts by domain (Productivity, Media, System).
- Mark every input/output type explicitly; never leave as “Anything”.
- Prefer “Run JavaScript for Automation” over “Run AppleScript” for new logic.
- Share shortcuts as `.shortcut` files and commit to git; describe required permissions in README.
Automator
- Use “Application” workflows for drag-and-drop, “Quick Action” for contextual menus.
- Limit to ≤ 10 actions; offload complex logic to scripts.
- Annotate each action with a comment.
Keyboard Maestro
- Prefix macro names with scope emoji: 🌐Global, 🖥️App, 🔄Window.
- Store all macros in XML and track changes in git.
- Use the “Prompt for User Input” action instead of pauses where possible.
BetterTouchTool
- Use conditional activation groups instead of per-app duplicates.
- Keep gesture names consistent: <device>-<action>-<target> (e.g., trackpad-3f-swipe-left-next-tab).
Raycast
- Package scripts as Extensions; include `command.json` metadata with an explicit icon.
- Return JSON in `raycast://messages`; fallback to stdout for logs only.
Testing
- Each workflow must include a Test Plan.md with: purpose, preconditions, expected results.
- Create a dummy environment (~/Automation/test_data) referenced via $AUTOMATION_ENV variable.
- Add a GitHub Action (macos-latest) that runs all JXA unit tests with `osacompile` to catch syntax errors.
Performance
- Cache expensive queries (e.g., Contacts search) in ~/Library/Caches/Automation for 5 minutes.
- Use `NSProcessInfo.processInfo.activeProcessorCount` to parallelize shell loops when batch processing files.
Security
- Never hard-code tokens; read from Keychain via `security find-generic-password` inside JXA.
- Request accessibility & automation permissions in a pre-flight script; exit with guidance if denied.
Documentation
- Generate HTML docs from inline JSDoc using `jsdoc -c jsdoc.json`; deploy to GitHub Pages.
- Provide a top-level README with flow diagrams (draw.io) for non-tech stakeholders.