LUNAROPS · OPERATIONAL UPLINK 100% UPTIME 1,247d POSTS 893 JEFF.MOON@LUNAROPS.DEV UTC --:--:--

macOS Power User Tips and Hidden Features

macosproductivityautomationhammerspoonhomebrewterminalpower-user

Most people learn a handful of macOS shortcuts, install a few apps from the App Store, and call it done. That is a perfectly functional setup, but it leaves enormous amounts of capability sitting idle. macOS is built on decades of thoughtful engineering, and the further you dig, the more you find: a scriptable automation framework that exposes nearly every system API to Lua, a command-line defaults system that unlocks hundreds of hidden behaviors, a Quick Look preview architecture that developers can extend with plugins, and a suite of built-in CLI tools that most users have never heard of.

This is not a list of party tricks. Every tip here is something that, once you internalize it, changes how you interact with your machine on a daily basis. If you have been using macOS for years and still reach for the mouse more than you should, still copy things to a text editor to transfer them between apps, still drag windows around by hand — this is the guide for you.


Quick Look and Its Extension Ecosystem

Quick Look is one of those features so well-integrated you stop noticing it is there. Press Space in Finder and you get a full-size, interactive preview of almost any file — PDFs, images, videos, audio files, web archives — without launching any application. It is instant. What most users do not realize is that Quick Look is a plugin architecture, and developers can register extensions that teach it to render new file types.

Out of the box, Quick Look handles the common cases. Where it falls short is the developer workflow: source code files, markdown documents, JSON, CSV, archives, and assorted file types without standard extensions. That is where the plugin ecosystem earns its keep.

Installing Quick Look plugins via Homebrew

The fastest way to get a capable Quick Look setup is via Homebrew Cask:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# Modern, actively maintained syntax highlighter (recommended over qlcolorcode)
brew install --cask syntax-highlight

# The classic option, still works for older macOS versions
brew install --cask qlcolorcode

# Preview files with no extension (Makefile, .zshrc, Podfile, etc.)
brew install --cask qlstephen

# Render Markdown as formatted HTML in Quick Look
brew install --cask qlmarkdown

# Syntax-highlighted JSON and formatted JSON tree
brew install --cask quicklook-json

# Show image dimensions and file size in the title bar
brew install --cask qlimagesize

# Preview .ase (Adobe Swatch Exchange) color palette files
brew install --cask quicklookase

# Preview zip, tar, and other archive contents
brew install --cask suspicious-package

# Preview video files with thumbnails
brew install --cask qlvideo

After installing cask plugins, run qlmanage -r to reset the Quick Look server and force it to pick up the new extensions. If a preview still does not render correctly, qlmanage -r cache clears the thumbnail cache.

Quick Look plugin reference

Plugin Brew Cask Name What It Renders
Syntax Highlight syntax-highlight Source code with themes, 300+ languages
QLColorCode qlcolorcode Source code (uses Highlight lib)
QLStephen qlstephen Plain text files with no extension
QLMarkdown qlmarkdown Markdown as rendered HTML
QuickLook JSON quicklook-json JSON with syntax color and tree view
QLImageSize qlimagesize Images with dimensions in title bar
QuickLookASE quicklookase Adobe color swatch (.ase) files
Suspicious Package suspicious-package .pkg installer contents
QLVideo qlvideo Video thumbnails and previews

The sandbox problem

macOS 12 Monterey and later enforced stricter sandbox rules on Quick Look extensions. The old-style .qlgenerator plugin bundles that lived in ~/Library/QuickLook were effectively deprecated in favor of App Extensions — plugins distributed inside app bundles, subject to the full sandbox. This broke a number of older plugins that relied on calling external processes (like the Highlight binary that QLColorCode used under the hood).

The practical outcome: prefer syntax-highlight (sbarex’s SourceCodeSyntaxHighlight) over the older qlcolorcode on modern macOS. Syntax Highlight was rebuilt from scratch as a proper App Extension with its own XPC service that handles the rendering in a sandboxed context without breaking. If you install a plugin and nothing happens, it is almost certainly a sandbox compatibility issue with an unmaintained package.

Invoking Quick Look from the terminal

qlmanage is the command-line interface to the Quick Look system. Beyond resetting the server, it lets you trigger previews programmatically:

1
2
3
4
5
6
7
8
# Open a Quick Look preview for a specific file
qlmanage -p /path/to/file.md

# Generate a Quick Look thumbnail to a file
qlmanage -t -s 512 -o /tmp/ /path/to/file.png

# Test which generator handles a UTI
qlmanage -m public.source-code

The -p flag is useful in scripts when you want to inspect a generated file without opening a dedicated application. Combine it with find to preview the first match of a pattern, or wire it to a shell alias for common inspection tasks.


Automator and the Shortcuts App

These two tools are not in competition — they occupy different niches in the macOS automation stack, and understanding which one to reach for which situation saves real time.

Automator: the legacy workhorse

Automator has been in macOS since Tiger (10.4) and it shows. The interface is dated, the action library is inconsistent, and some of its more ambitious record-and-replay features were never quite reliable. But it is still irreplaceable for one thing: folder actions. A folder action is an Automator workflow attached to a folder that fires automatically when files are added. Drop a file into a watched folder, and the workflow runs. Rename, convert, move, process, compress — anything Automator can do, the folder action will do automatically.

Creating a folder action: open Automator, choose “Folder Action” as the document type, pick the folder to watch at the top, then build your action chain. Common useful chains:

  • Watch a ~/Inbox folder, run an OCR action, move to ~/Documents/OCR
  • Watch a ~/ToConvert folder, resize images to web dimensions, export to a separate folder
  • Watch a download folder for specific file types and auto-sort them

Automator workflows can also be saved as Applications (double-click to run), Services (appear in right-click context menus), or Quick Actions (appear in Finder’s Quick Actions section in the preview pane).

Shortcuts: the modern layer

The Shortcuts app arrived on macOS in Monterey, ported from iOS. It runs the same shortcut definition across iPhone, iPad, Mac, Apple Watch, and HomePod — which is genuinely powerful when you want a single automation that runs everywhere. On macOS specifically, a shortcut can be triggered from the menu bar, from Spotlight (just type the shortcut name), from a keyboard shortcut assigned in the shortcut editor, or from the command line.

The shortcuts CLI:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# List all shortcuts
shortcuts list

# Run a shortcut by name
shortcuts run "Convert HEIC to JPEG"

# Run a shortcut and pass input via stdin
echo "https://example.com" | shortcuts run "Extract Article Text"

# Run with a file as input
shortcuts run "Resize Image" --input-path ~/Desktop/photo.heic

This makes Shortcuts composable with shell scripts. A long build process can end with shortcuts run "Build Complete Notification" to trigger a notification or play a sound. A cron job can invoke shortcuts run "Weekly Report Template" every Monday morning.

Practical Shortcuts to build

Batch convert HEIC to JPEG: Select files in Finder, invoke via Services menu. The shortcut uses “Get the input from” set to shortcut input (file), adds a “Convert Image” action set to JPEG at 85% quality, then “Save File” to a destination folder.

Resize images for web: Same structure — accept files, “Resize Image” action set to fit within 1920x1080, save to a ~/Processed folder. Wire it to Cmd+Shift+R in the Shortcuts keyboard shortcut field and it becomes instantaneous.

Combine PDFs: Accept multiple PDF files, use “Combine PDF” action, save with a datestamped filename. This replaces the drag-to-Preview workaround most people use.

Extract URLs from selected text: Accept text input, use “Get URLs from Input”, then “Copy to Clipboard” or pass to the browser. Useful when staring at a wall of text with embedded links.

Meeting note template from calendar: Use “Find Calendar Events” with a filter for the next 30 minutes, extract the title and attendees, format into a markdown template, open in your editor. Trigger it as you sit down for a meeting.

macOS Tahoe and folder automation in Shortcuts

macOS 26 (Tahoe) brought folder automation natively to Shortcuts — the long-awaited modern replacement for Automator folder actions. You can now trigger a Shortcut when items are added to a specified folder, without needing Automator as the intermediary. This is the future of macOS folder automation, though Automator folder actions remain more battle-tested for production workflows right now. If you are setting up a new automation and targeting Tahoe+, use Shortcuts. For anything targeting Ventura or Sonoma, Automator folder actions are still the more reliable choice.

When Automator still wins:

  • Folder actions on macOS older than Tahoe
  • Workflows that need to run as standalone applications with a double-click
  • Complex multi-step document processing with legacy Automator actions
  • When you need the workflow to work in environments where Shortcuts is unavailable

When Shortcuts wins:

  • Cross-device synchronization
  • Sharing automations with non-technical users
  • Triggering via Siri, menu bar, or keyboard shortcut
  • Integrations with modern apps that have added Shortcuts support
  • Anything you want to trigger from the CLI in a script

Hot Corners

Hot corners are one of those features that seems trivial until you set them up correctly, and then you wonder how you ever lived without them. Navigate to System Settings > Desktop & Dock > Hot Corners. Each corner of the screen can trigger an action when you throw the cursor into it.

Available actions per corner:

Action Description
Mission Control Overview of all open windows and spaces
Application Windows Exposé for the current app
Desktop Hide all windows, show desktop
Notification Center Slide in the notification sidebar
Launchpad Show app grid
Quick Note Open a new Quick Note immediately
Screen Saver Start screen saver
Lock Screen Lock the screen and require password
Sleep Display Turn off the display without sleeping
Put Display to Sleep Same as above
Disable Screen Saver Keep screensaver from activating

A practical layout that works well for desk-bound work:

+------------------+------------------+
| Mission Control  | Lock Screen      |
| (top-left)       | (top-right)      |
+------------------+------------------+
| Quick Note       | Desktop          |
| (bottom-left)    | (bottom-right)   |
+------------------+------------------+

This gives you: instant workspace overview top-left (muscle memory for when you are juggling multiple contexts), lock screen top-right (reach the corner when stepping away), Quick Note bottom-left for capturing thoughts without breaking flow, and Desktop bottom-right to reveal what is on the desktop without minimizing anything.

The Option modifier

Every hot corner action can be made to require the Option key by holding Option while clicking the popup in the Hot Corners configuration panel. The corner will display a small option symbol next to the action name. With the Option modifier set, throwing your cursor into the corner does nothing — you have to be holding Option simultaneously for the action to fire.

This is the solution to the problem that keeps people from using hot corners: accidental triggers. If your bottom-right corner triggers Desktop, you will constantly expose your desktop when moving to click something near the edge of the screen. With Option required, the corner is effectively disabled unless you specifically intend to activate it. Set your most disruptive actions (Lock Screen, Sleep Display) to require Option, and keep the non-destructive actions (Mission Control, Quick Note) as bare triggers.

Hot corners replace a surprising number of menu bar interactions. Lock screen is otherwise Menu bar > Apple menu > Lock Screen, or Cmd+Ctrl+Q. Mission Control is F3 or a three-finger swipe. Desktop is a five-finger spread. Quick Note requires finding the icon in Control Center. Once the muscle memory for hot corners is there, all of those paths feel slow.


Clipboard History

macOS stores exactly one item in its system clipboard. Copy something new and the previous item is gone. This is a genuine limitation with no built-in workaround, and it is one of the few places where Windows and Linux have had parity or better for years.

The solution is a third-party clipboard manager. There are several good options, and they differ in meaningful ways.

Maccy

Maccy is the recommendation for most users. It is free, open source (MIT license), available directly on GitHub or via Homebrew Cask (brew install --cask maccy), and it does exactly one thing: maintain a clipboard history. The default history size is 200 items, configurable up to 999. Activating it is Cmd+Shift+V (configurable), which opens a small popup with fuzzy search across your entire history. Type a few characters from anything you copied recently and it filters instantly. Click or press Enter to paste.

Key Maccy behaviors worth configuring:

  • Enable “Paste automatically” so selecting a history item pastes it directly rather than just copying to clipboard
  • Set history size to 999 items — there is no performance cost
  • Enable “Show in menu bar” for quick visual access
  • Configure “Ignore” patterns for passwords (Maccy can ignore clipboard changes from specific applications like 1Password or Keychain entries)

Privacy matters here. Maccy stores everything locally. No sync, no cloud backup, no telemetry. If you copy a password, an API key, or sensitive document text, it lives in Maccy’s local database and nowhere else. Cloud-synced clipboard managers (the kind that sync via iCloud across devices) should be treated with significant caution for any workflow that touches credentials or confidential information. Maccy’s local-only model is the right default.

Raycast clipboard history

If you are already running Raycast as your launcher, its built-in clipboard history is an excellent option. The free tier includes clipboard history with support for text, images, colors, and links, pinning, and search. History is kept for up to 3 months and stored encrypted locally. The integration with Raycast’s launcher means you can search clipboard history in the same interface you use for everything else.

If you are not already using Raycast, installing it solely for clipboard history is overkill — Maccy is a fraction of the footprint.

Other alternatives

  • Pasta: polished UI, paid ($9.99), shows clipboard history in a visual grid
  • CopyClip: free, minimal, Menu Extra only, no fuzzy search
  • Alfred Clipboard: powerful if you are in the Alfred ecosystem, part of the Alfred Powerpack ($34 one-time)

Plain-text paste

A related pain point: when you paste rich text (HTML, RTF) into a document, you often get the source formatting rather than plain text. Some apps handle this with Cmd+Shift+Option+V (paste and match style), but support is inconsistent. Maccy solves this cleanly — there is a separate “Paste as Plain Text” action in the popup, and you can set a dedicated keyboard shortcut for it. This alone justifies having a clipboard manager.


Hammerspoon

Hammerspoon is the tool that, once you start using it, makes every other automation approach on macOS feel limited. It is a Lua scripting engine with deep, native bindings to macOS system APIs: windows and frames, applications, hotkeys, menus, the notification center, network events (WiFi SSID changes, interface transitions), USB attach/detach events, battery state, audio devices, drawing arbitrary graphics on screen, HTTP requests, file system watching, idle detection, and more.

Install it:

1
brew install --cask hammerspoon

The entry point is ~/.hammerspoon/init.lua. Every time you save that file and reload the configuration (either via the Hammerspoon menu bar icon or Cmd+Shift+R if you wire that up), Hammerspoon re-evaluates the entire file. The reload cycle is fast enough that it feels like live scripting.

+-------------------------------------------+
|           Hammerspoon Architecture        |
+-------------------------------------------+
|  ~/.hammerspoon/init.lua                  |
|       |-- requires Spoons (modules)       |
|       |-- hs.hotkey bindings              |
|       |-- hs.window rules                 |
|       |-- hs.application watchers         |
|       \-- hs.network.reachability watchers|
+-------------------------------------------+
|           Hammerspoon Runtime             |
|  LuaJIT + Objective-C bridge              |
+-------------------------------------------+
|           macOS System APIs               |
|  Accessibility API, CoreGraphics,         |
|  AppKit, SystemConfiguration, IOKit       |
+-------------------------------------------+

Why Hammerspoon instead of AppleScript?

AppleScript is ancient, syntactically tortured, inconsistently supported across applications, and slow. Hammerspoon’s Lua API is documented, fast, and covers system-level operations that AppleScript cannot reach at all (network events, USB, screen drawing). For anything beyond the most basic document manipulation in apps that explicitly support AppleScript, Hammerspoon is the right tool.

Window layout automation

The most immediately useful application of Hammerspoon is deterministic window layout. Instead of manually dragging windows to approximately the right position, you write a rule once and bind it to a key.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
-- Move focused window to left half of screen
hs.hotkey.bind({"cmd", "alt", "ctrl"}, "Left", function()
    local win = hs.window.focusedWindow()
    local screen = win:screen():frame()
    win:setFrame({
        x = screen.x,
        y = screen.y,
        w = screen.w / 2,
        h = screen.h
    })
end)

-- Move focused window to right half
hs.hotkey.bind({"cmd", "alt", "ctrl"}, "Right", function()
    local win = hs.window.focusedWindow()
    local screen = win:screen():frame()
    win:setFrame({
        x = screen.x + screen.w / 2,
        y = screen.y,
        w = screen.w / 2,
        h = screen.h
    })
end)

-- Maximize window
hs.hotkey.bind({"cmd", "alt", "ctrl"}, "Up", function()
    local win = hs.window.focusedWindow()
    win:maximize()
end)

For multi-display layouts, use hs.layout.apply() with a layout table:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
local layout = {
    -- {app name, window title, screen, unit rect, frame, fullscreen}
    {"iTerm2",    nil, hs.screen.primaryScreen(), hs.layout.left50,    nil, nil},
    {"Safari",    nil, hs.screen.primaryScreen(), hs.layout.right50,   nil, nil},
    {"Slack",     nil, hs.screen.allScreens()[2], hs.layout.maximized, nil, nil},
    {"Mail",      nil, hs.screen.allScreens()[2], hs.layout.maximized, nil, nil},
}

hs.hotkey.bind({"cmd", "alt", "ctrl"}, "W", function()
    hs.layout.apply(layout)
end)

Press the key and every window snaps to its defined position. For people who work on a laptop that regularly docks and undocks, this transforms the reconnect experience.

The Hyper key

One of the most popular Hammerspoon patterns is the “Hyper key” — repurposing Caps Lock (which almost nobody uses) into a new modifier key that can be combined with any other key for a large namespace of custom shortcuts.

The cleanest implementation uses Karabiner-Elements to remap Caps Lock to F18 (a key that exists but has no default binding on modern keyboards), then Hammerspoon picks up F18 presses:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
-- ~/.hammerspoon/init.lua
-- Karabiner maps Caps Lock -> F18
-- We use F18 as our "hyper" key

local hyper = {"ctrl", "alt", "cmd", "shift"}

-- Hyper + H/J/K/L for window movement (vim-style)
hs.hotkey.bind(hyper, "H", function()
    local win = hs.window.focusedWindow()
    local screen = win:screen():frame()
    win:setFrame({x=screen.x, y=screen.y, w=screen.w/2, h=screen.h})
end)

hs.hotkey.bind(hyper, "L", function()
    local win = hs.window.focusedWindow()
    local screen = win:screen():frame()
    win:setFrame({x=screen.x+screen.w/2, y=screen.y, w=screen.w/2, h=screen.h})
end)

-- Hyper + app shortcuts to launch or focus applications
hs.hotkey.bind(hyper, "T", function()
    hs.application.launchOrFocus("iTerm")
end)

hs.hotkey.bind(hyper, "B", function()
    hs.application.launchOrFocus("Safari")
end)

hs.hotkey.bind(hyper, "S", function()
    hs.application.launchOrFocus("Slack")
end)

The result is a clean namespace. Hyper+T always goes to the terminal. Hyper+B always goes to the browser. Hyper+H/L snap the current window. None of these conflict with anything because Ctrl+Alt+Cmd+Shift is not used by any application.

WiFi-triggered automation

Hammerspoon can watch for network events and run code when you join or leave a known network:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
local wifiWatcher = nil
local homeSSID = "HomeNetwork"

local function ssidChanged()
    local newSSID = hs.wifi.currentNetwork()
    if newSSID == homeSSID then
        -- Connected to home: unmute, disable VPN notification
        hs.audiodevice.defaultOutputDevice():setOutputVolume(50)
        hs.notify.new({title="Hammerspoon", informativeText="Welcome home"}):send()
    else
        -- Left home or joined other network: enable different profile
        hs.notify.new({title="Hammerspoon", informativeText="Network: " .. (newSSID or "none")}):send()
    end
end

wifiWatcher = hs.wifi.watcher.new(ssidChanged)
wifiWatcher:start()

Practical applications: change audio output when joining the office network, enable/disable proxy settings, mount/unmount network shares, change wallpaper based on location.

Application-specific hotkeys

Hammerspoon can scope hotkeys to fire only when a specific application has focus:

1
2
3
4
5
6
7
8
9
-- Custom shortcuts only active in Finder
local finderFilter = hs.window.filter.new("Finder")

finderFilter:subscribe(hs.window.filter.windowFocused, function()
    hs.hotkey.bind({"cmd"}, "Delete", function()
        -- Move to trash with keyboard shortcut override
        hs.eventtap.keyStroke({"cmd"}, "Delete")
    end)
end)

The more common pattern is using hs.application.watcher to detect app focus changes and toggle a modal hotkey namespace:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
local appHotkeys = {}

local function applyHotkeys(appName, eventType)
    if eventType == hs.application.watcher.activated then
        if appName == "Xcode" then
            appHotkeys[1] = hs.hotkey.bind({"cmd","shift"}, "R", function()
                -- custom Xcode build action
            end)
        end
    elseif eventType == hs.application.watcher.deactivated then
        for _, hk in ipairs(appHotkeys) do hk:disable() end
        appHotkeys = {}
    end
end

local appWatcher = hs.application.watcher.new(applyHotkeys)
appWatcher:start()

This is the kind of capability that no other macOS automation tool offers cleanly. AppleScript cannot do it. Shortcuts cannot do it. Keyboard Maestro can approximate it but at significant complexity and cost.


defaults write for Hidden Preferences

Every macOS application and system component stores its preferences in a key-value database managed by the defaults system. The command-line interface to this system lets you read, write, and delete preference keys — including undocumented ones that Apple’s engineers used during development and never exposed in the UI.

The basic commands:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# Read all defaults for an app
defaults read com.apple.dock

# Read a specific key
defaults read com.apple.finder AppleShowAllFiles

# Write a value
defaults write com.apple.dock autohide-delay -float 0

# Delete a key (revert to system default)
defaults delete com.apple.dock autohide-delay

# Read global (cross-app) preferences
defaults read NSGlobalDomain

Bundle IDs follow the reverse-domain pattern: com.apple.dock, com.apple.finder, com.apple.screencapture. Most changes require killing and restarting the affected process before they take effect.

Essential defaults write commands

Command Effect Restart Required
defaults write com.apple.dock autohide-delay -float 0 Eliminate Dock autohide delay killall Dock
defaults write com.apple.dock autohide-time-modifier -float 0.15 Faster Dock slide animation killall Dock
defaults write com.apple.finder AppleShowAllFiles -bool true Show hidden files in Finder killall Finder
defaults write com.apple.finder ShowPathbar -bool true Show path bar at bottom of Finder killall Finder
defaults write com.apple.finder ShowStatusBar -bool true Show status bar in Finder killall Finder
defaults write com.apple.finder _FXShowPosixPathInTitle -bool true Full POSIX path in Finder window title killall Finder
defaults write com.apple.desktopservices DSDontWriteNetworkStores -bool true No .DS_Store on network volumes None (immediate)
defaults write com.apple.desktopservices DSDontWriteUSBStores -bool true No .DS_Store on USB volumes None (immediate)
defaults write NSGlobalDomain NSNavPanelExpandedStateForSaveMode -bool true Expand save dialog by default None
defaults write NSGlobalDomain NSDocumentSaveNewDocumentsToCloud -bool false Save to disk by default, not iCloud None
defaults write com.apple.screencapture type jpg Screenshots as JPEG instead of PNG None
defaults write com.apple.screencapture location ~/Pictures/Screenshots Custom screenshot save location None
defaults write com.apple.screencapture disable-shadow -bool true Remove window shadow from screenshots None
defaults write NSGlobalDomain ApplePressAndHoldEnabled -bool false Disable press-and-hold accent menu (re-enables key repeat) None
defaults write com.apple.Safari IncludeInternalDebugMenu -bool true Enable Safari debug menu Relaunch Safari
defaults write com.apple.mail DisableInlineAttachmentViewing -bool true Attachments as icons, not inline Relaunch Mail

A note on Gatekeeper

You will encounter guides suggesting sudo spctl --master-disable or defaults write com.apple.LaunchServices LSQuarantine -bool false to disable Gatekeeper. Be deliberate about this. Gatekeeper prevents execution of unsigned/unnotarized software and is a meaningful security control. Disabling it system-wide is a broad change with real security implications. The right approach for running unsigned software is right-click > Open the first time, which creates a permanent exception for that specific binary while leaving Gatekeeper otherwise intact.

Reverting changes

Every defaults write has a corresponding defaults delete:

1
2
3
# Remove the autohide-delay override, restoring system default
defaults delete com.apple.dock autohide-delay
killall Dock

If you have been liberal with defaults write and want a clean slate for a particular application’s preferences, defaults delete com.apple.dock (without a key name) wipes the entire preference domain and resets all dock settings to defaults. Do this carefully — it is not easily undoable.


Essential Built-in CLI Tools

macOS ships with a collection of CLI tools that do not exist on Linux and are not well-known even among experienced terminal users. These are not replacements for the standard Unix toolkit — they are macOS-specific utilities that hook into system capabilities.

pbcopy and pbpaste

These two commands bridge the terminal and the system clipboard. pbcopy reads from stdin and places the content on the clipboard. pbpaste writes the clipboard content to stdout.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# Copy file content to clipboard
cat ~/.ssh/id_ed25519.pub | pbcopy

# Copy command output to clipboard
git log --oneline -20 | pbcopy

# Paste clipboard into a file
pbpaste > output.txt

# Use clipboard content as input to another command
pbpaste | grep "ERROR"

# Copy the current working directory path
pwd | pbcopy

pbcopy and pbpaste handle Unicode, binary data, and multi-line content correctly. They are indispensable for scripting workflows that interact with the GUI clipboard.

open

open launches files, directories, and URLs from the terminal using the system’s default application associations — the same logic as double-clicking in Finder.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# Open current directory in Finder
open .

# Open a file with its default application
open report.pdf

# Open with a specific application
open -a "Visual Studio Code" .
open -a "TextEdit" ~/notes.txt

# Open a URL in the default browser
open https://example.com

# Open a URL in a specific browser
open -a "Firefox" https://example.com

# Open in background (don't bring app to foreground)
open -g ~/Documents

# Reveal a file in Finder without opening it
open -R ~/Documents/report.pdf

The -a flag is the most powerful option. open -a "Visual Studio Code" . from any directory is a reliable way to open a project in your editor. Wire it to a shell function named code if VS Code’s CLI is not in your path.

mdfind

mdfind is Spotlight from the command line. It queries the same index that the Spotlight UI uses, making it fast and comprehensive — it finds files by content, metadata attributes, and name, all indexed and near-instant.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# Find files by name (equivalent to Spotlight name search)
mdfind -name "quarterly-report"

# Full Spotlight query
mdfind "kubernetes deployment"

# Search within a specific directory
mdfind -onlyin ~/Documents "invoice 2025"

# Find by file type using UTI
mdfind "kMDItemContentType == 'com.adobe.pdf'"

# Find images modified in the last 7 days
mdfind "kMDItemKind == 'Image' && kMDItemFSCreationDate >= \$time.now(-7d)"

# Find files containing a string (content search)
mdfind -interpret "error handling swift"

mdfind is meaningfully different from find. find traverses the filesystem in real-time; mdfind queries an index. For files within indexed locations (your home directory, most common paths), mdfind is orders of magnitude faster. For files in excluded locations (external volumes not indexed by Spotlight, /tmp, paths added to Spotlight’s Privacy list), find is the right tool.

caffeinate

caffeinate prevents macOS from sleeping. Indispensable for long-running processes, downloads, or any time you need the machine to stay awake without touching System Settings.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# Prevent sleep for 1 hour
caffeinate -t 3600

# Prevent sleep while a command runs (process-bound)
caffeinate -i make clean all

# Prevent display sleep (weaker than full system caffeinate)
caffeinate -d

# Keep system awake indefinitely (Ctrl+C to stop)
caffeinate

# Prevent system sleep AND disk sleep
caffeinate -im

The process-bound form (caffeinate -i command) is the most ergonomic for scripting: the caffeinate process exits automatically when the wrapped command finishes, so you cannot forget to re-enable sleep. Use it for large builds, database migrations, backup jobs, or any process where sleeping mid-run would cause corruption or timeout.

pmset

pmset is the power management utility. It reads and writes the same settings that System Settings > Battery exposes, plus some that are not exposed in the UI at all.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# Show all current power settings
pmset -g

# Show live power source status
pmset -g ps

# Show sleep/wake history
pmset -g log | grep -E "Wake|Sleep"

# Put the display to sleep immediately
pmset displaysleepnow

# Put the system to sleep immediately
pmset sleepnow

# Set display sleep timer (minutes) for battery
pmset -b displaysleep 5

# Set display sleep timer for AC power
pmset -c displaysleep 15

# Disable Power Nap while on battery (can help battery life)
sudo pmset -b powernap 0

# Schedule a wake time
sudo pmset schedule wake "05/31/2026 07:00:00"

say

say sends text to macOS’s speech synthesis engine. The legitimate power user application: audible notifications at the end of long-running terminal commands.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# Speak when a build completes
make all && say "Build complete" || say "Build failed"

# Speak arbitrary text
say "Deployment finished. Check the logs."

# Use a specific voice
say -v "Samantha" "Tests are passing"

# List available voices
say -v "?"

# Save speech to an audio file
say -o output.aiff "This is a test"

Chaining say to the end of a long command is a practical alternative to notification-based alerting when you are not watching the terminal.

sips

sips (Scriptable Image Processing System) is a powerful image processing tool that ships with macOS and handles format conversion, resizing, rotating, and metadata manipulation without requiring ImageMagick or any external dependency.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# Resize an image to fit within 800x600 (maintains aspect ratio)
sips -Z 800 image.png

# Explicit width and height (may distort if ratios differ)
sips -z 600 800 image.png

# Convert format
sips -s format jpeg image.heic --out image.jpg

# Batch convert all HEIC files in current directory to JPEG
for f in *.heic; do sips -s format jpeg "$f" --out "${f%.heic}.jpg"; done

# Get image info
sips -g all image.png

# Rotate 90 degrees clockwise
sips -r 90 image.png

textutil

textutil converts between document formats. The primary use case: converting Word documents to plain text or HTML without opening Microsoft Word or Pages.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# Convert .docx to plain text
textutil -convert txt document.docx

# Convert to HTML
textutil -convert html document.docx

# Convert to PDF (via intermediate RTF)
textutil -convert rtf document.docx

# Concatenate multiple documents
textutil -cat html *.rtf -output combined.html

Built-in CLI tools reference

Tool Description Example
pbcopy Pipe stdin to clipboard pwd | pbcopy
pbpaste Clipboard to stdout pbpaste | wc -l
open Open files/URLs/apps open -a "VS Code" .
mdfind Spotlight from terminal mdfind -name "report"
caffeinate Prevent sleep caffeinate -i make
pmset Power management pmset -g log
say Text-to-speech make && say "done"
sips Image processing sips -Z 800 photo.png
textutil Document conversion textutil -convert txt file.docx
networksetup Network configuration networksetup -listallnetworkservices
scutil System config (hostname, DNS) scutil --get ComputerName
screencapture Screenshot from CLI screencapture -T 5 screen.png
diskutil Disk management diskutil list
softwareupdate CLI for software updates softwareupdate -l

Essential Homebrew Formulae for Power Users

The standard Homebrew introduction covers installing wget, git, and maybe node. That is fine as a starting point but barely scratches the surface. The formulae that genuinely transform daily terminal usage are the modern reimplementations of classic Unix tools — written in Rust or Go, with sensible defaults, color output, and performance characteristics that make the originals feel antiquated.

Install all of these at once:

1
brew install bat eza ripgrep fd fzf zoxide git-delta jq yq htop btop tldr watch gnu-sed coreutils

bat

bat is cat with syntax highlighting, line numbers, Git change indicators, and automatic paging. Drop-in replacement for cat in most contexts.

1
2
3
bat ~/.hammerspoon/init.lua       # Syntax highlighted Lua
bat -n Makefile                   # Line numbers only, no paging
bat --style=plain file.py         # Plain output (good for piping)

Add alias cat="bat" to your shell config if you want it as the default. Some scripts depend on cat’s minimal output, so keep the alias opt-in rather than mandatory.

eza

eza is a modern replacement for ls written in Rust. Color-coded by file type, shows Git status, understands symlinks, and has a tree view built in.

1
2
3
4
eza -la                           # Long listing with all files
eza --tree --level=2              # Tree view, 2 levels deep
eza -la --git                     # Include Git status column
eza --sort=modified               # Sort by modification time

Aliases that work well: alias ls="eza", alias ll="eza -la", alias tree="eza --tree".

ripgrep (rg)

rg is faster than grep in nearly every benchmark on real codebases. It respects .gitignore by default, skips hidden files and binary files, and outputs with color and line numbers out of the box.

1
2
3
4
5
rg "function handleAuth" src/       # Recursive search
rg -t py "import asyncio"           # Restrict to Python files
rg -l "TODO"                        # List files only, not matches
rg "error" --ignore-case -C 2       # Case-insensitive, 2 lines context
rg "panic!" --glob "*.rs"           # Glob pattern for files

If you have been using grep -r for project-wide search, rg is a direct and significantly faster replacement.

fd

fd is a simpler, faster find. The syntax is more intuitive, it respects .gitignore, and it runs searches in parallel.

1
2
3
4
fd "*.md" docs/                    # Find markdown files under docs/
fd -e py -e js src/                # Find by multiple extensions
fd -t d node_modules               # Find directories named node_modules
fd --changed-within 1d .           # Files changed in the last day

fzf — the force multiplier

fzf is a command-line fuzzy finder that deserves special attention because of what it does to other tools. On its own, it is an interactive filter: pipe anything to it, type to narrow down the list, press Enter to output the selected line. In combination with other tools and shell integration, it transforms how you interact with command history, files, and processes.

Run the fzf shell integration installer after installing:

1
$(brew --prefix)/opt/fzf/install

This adds three shell integrations that change daily terminal usage immediately:

  • Ctrl+R — reverse history search with interactive fuzzy matching instead of the default incremental search. Type fragments of any command you have run, anywhere in the string, and instantly filter to it.
  • Ctrl+T — fuzzy file picker. Triggers at any point in a command: type vim, press Ctrl+T, navigate to a file interactively, and the path is inserted.
  • Alt+C — fuzzy directory jump. Type to filter directories and change into the selected one.

Beyond the shell integration, fzf composes with everything:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# Kill a process by fuzzy-selecting from ps output
kill $(ps aux | fzf | awk '{print $2}')

# Open a file in your editor, selected interactively
$EDITOR $(fd -t f | fzf --preview 'bat --color=always {}')

# Check out a git branch interactively
git checkout $(git branch | fzf)

# SSH to a host, selected from your known_hosts
ssh $(grep -oE "^[^ ,]+" ~/.ssh/known_hosts | fzf)

The preview window option (--preview) integrates with bat to show syntax-highlighted file content as you navigate.

zoxide

zoxide builds a frecency database of directories you visit (frequency × recency) and lets you jump to them with minimal typing.

1
2
3
4
5
6
7
8
9
# After initializing (add to shell rc):
eval "$(zoxide init zsh)"   # or bash/fish

# Jump to any visited directory by fragment
z lua          # jumps to most frecent dir matching "lua"
z proj api     # multiple fragments, narrows further

# Interactive mode (requires fzf)
zi            # fuzzy picker over your frecency database

After a day of normal work, zoxide knows all your common directories and you stop typing full paths entirely.

delta

delta is a syntax-highlighting pager for git diff. Configure it as Git’s pager and every git diff, git show, and git log -p becomes dramatically more readable.

1
2
3
4
5
# ~/.gitconfig additions
git config --global core.pager delta
git config --global delta.navigate true
git config --global delta.side-by-side true
git config --global delta.line-numbers true

jq and yq

jq is the standard tool for processing JSON from the command line. yq extends the same query syntax to YAML and TOML. Both are essential for anyone dealing with APIs, configuration files, or Kubernetes manifests.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# Extract a nested field
curl -s api.example.com/data | jq '.users[].email'

# Filter and reformat
cat config.json | jq '{name: .metadata.name, image: .spec.image}'

# Process Kubernetes manifests
kubectl get pod my-pod -o json | jq '.spec.containers[].resources'

# YAML equivalent
yq '.services.web.image' docker-compose.yml
yq -i '.replicas = 3' deployment.yaml   # In-place edit

tldr

man pages are comprehensive but slow to navigate when you just need a quick example. tldr provides community-maintained simplified pages focused on common usage examples.

1
2
3
tldr tar         # Quick examples for tar
tldr rsync       # Common rsync patterns
tldr ffmpeg      # FFmpeg examples

watch

watch periodically re-runs a command and displays the output, refreshing in place. It exists on Linux but not in macOS’s base install.

1
2
3
watch -n 2 'kubectl get pods'          # Refresh pod list every 2 seconds
watch -n 5 'git log --oneline -10'     # Watch recent commits
watch 'pmset -g ps'                    # Monitor power state

Screen Capture and Annotation

macOS has a layered screenshot system that most users only partially know. The full toolkit is more capable than it appears.

The keyboard shortcuts

Shortcut Action
Cmd+Shift+3 Capture full screen to file
Cmd+Shift+4 Capture selected region to file
Cmd+Shift+4, then Space Capture a specific window (click to select)
Cmd+Shift+5 Screenshot and recording control panel
Cmd+Shift+6 Capture Touch Bar (older MacBooks)
Add Ctrl to any above Capture to clipboard instead of file

The floating thumbnail that appears after a screenshot (bottom-right corner) is more capable than it looks. Click it to open the annotation editor before the screenshot saves. Swipe it left to dismiss and save immediately. Right-click it for options including saving to a different location, opening in an application, or sharing.

Screenshot.app (Cmd+Shift+5)

The full screenshot panel (Cmd+Shift+5) is where screen recording lives. The recording options are “Entire Screen” and “Selected Portion,” with a microphone selector for audio. Options include showing/hiding the mouse cursor in recordings and setting a countdown timer. The output is a .mov file; convert to MP4 with ffmpeg -i recording.mov -vcodec h264 recording.mp4 for better compatibility.

For window screenshots (Cmd+Shift+4 then Space), holding Option while clicking removes the window shadow from the screenshot — useful for documentation where the shadow creates visual clutter.

The screencapture CLI

screencapture is the terminal interface to the screenshot system:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# Capture entire screen
screencapture screen.png

# Capture with a 5-second delay
screencapture -T 5 delayed.png

# Interactive region selection
screencapture -i region.png

# Capture to clipboard instead of file
screencapture -c

# Capture specific window by its ID
screencapture -l $(osascript -e 'tell app "Safari" to id of window 1') window.png

# Capture as JPEG at 80% quality
screencapture -t jpg -q 80 output.jpg

This is particularly useful in automated testing or documentation generation workflows where you need screenshots without human interaction.

Annotation in Preview

For annotation without a third-party tool, Preview’s Markup toolbar is capable. Open a screenshot in Preview, show the Markup toolbar (View > Show Markup Toolbar or the pencil icon), and you have arrows, shapes, text, callouts, a redaction tool, a signature tool, and a magnify/zoom tool. It is not as fast as a dedicated annotation tool but it is always available and requires no additional software.

Third-party tools: CleanShot X vs Shottr

These are the two most widely recommended third-party screenshot tools, and they serve different needs.

CleanShot X is the more capable option. It adds scrolling capture (capture an entire webpage or long document by scrolling automatically), GIF recording, a cloud upload service that generates a shareable link in one click, a dedicated annotation mode with text styles, arrows, spotlight effects, and blur, and a “Quick Access Overlay” that pins your recent screenshots for quick reference. It is available on Setapp or as a direct purchase. For anyone who regularly annotates screenshots for documentation, support tickets, or tutorials, it is worth the cost.

Shottr is a 2MB single-window app with a strong focus on precision. It is notably fast (screenshot to annotation in under 200ms), has a color picker, screen ruler, pixel measurement tools, and OCR — right-click any screenshot element, select “Copy Text,” and it extracts the text. Shottr costs a one-time $12 after a 30-day free trial. For developers who need exact pixel measurements or need to copy text from screenshots regularly, Shottr is the better fit.


Login Items and Launch Agents

macOS starts background processes through two distinct mechanisms. Understanding the difference determines whether you look in System Settings or in the filesystem when something is starting unexpectedly — or when something that should start is not.

Login Items: the visible layer

Login items are the processes that appear in System Settings > General > Login Items. This section shows two categories: items that open at login (full applications, helper apps, menu extras) and items that are allowed to run in the background (background components registered by applications).

The “Allow in Background” section in particular grew dramatically in macOS Ventura and later as Apple required apps to register their background helpers through this new mechanism. If an application’s background functionality is broken, this is the first place to check — it may have been toggled off.

Launch Agents: the programmatic layer

Launch agents are plist files that tell launchd (the macOS init system, PID 1) to run a program. They are the macOS equivalent of systemd user services, cron jobs, and init scripts combined.

Key directories:

~/Library/LaunchAgents/          # Per-user agents (run as current user)
/Library/LaunchAgents/           # System-wide agents (run for all users)
/Library/LaunchDaemons/          # System daemons (run as root, no user session required)
/System/Library/LaunchAgents/    # Apple's own agents (do not modify)
/System/Library/LaunchDaemons/   # Apple's own daemons (do not modify)

Inspecting what is running

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# List all launch agents loaded for the current user
launchctl list

# Filter to running agents (non-zero PID)
launchctl list | awk '$1 != "-" {print}'

# Check a specific agent
launchctl list com.apple.Spotlight

# Print the full plist for a loaded agent
launchctl print gui/$(id -u)/com.some.agent

Loading and unloading agents

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# Load (enable) an agent
launchctl load ~/Library/LaunchAgents/com.example.myagent.plist

# Unload (disable) an agent until next login
launchctl unload ~/Library/LaunchAgents/com.example.myagent.plist

# Bootstrap (persistent enable, survives reboots) — macOS 10.11+
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.example.myagent.plist

# Bootout (persistent disable)
launchctl bootout gui/$(id -u) ~/Library/LaunchAgents/com.example.myagent.plist

Note: launchctl load/unload has been soft-deprecated in favor of bootstrap/bootout on modern macOS, but load/unload still works in practice for user-space agents.

Writing a custom launch agent

A launch agent plist is an XML property list. Here is a template for the three most common patterns:

Run a script at login:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<?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>Label</key>
    <string>com.yourname.startup-script</string>
    <key>ProgramArguments</key>
    <array>
        <string>/bin/bash</string>
        <string>/Users/yourname/scripts/startup.sh</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
    <key>StandardOutPath</key>
    <string>/tmp/startup-script.log</string>
    <key>StandardErrorPath</key>
    <string>/tmp/startup-script-error.log</string>
</dict>
</plist>

Run on a schedule (every 15 minutes):

1
2
<key>StartInterval</key>
<integer>900</integer>

Or with a cron-style calendar interval (9 AM every weekday):

1
2
3
4
5
6
7
8
9
<key>StartCalendarInterval</key>
<dict>
    <key>Hour</key>
    <integer>9</integer>
    <key>Minute</key>
    <integer>0</integer>
    <key>Weekday</key>
    <integer>1</integer>
</dict>

Run when a file or directory changes (watch mode):

1
2
3
4
<key>WatchPaths</key>
<array>
    <string>/Users/yourname/Dropbox/Inbox</string>
</array>

Save the plist to ~/Library/LaunchAgents/com.yourname.agentname.plist and load it with launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.yourname.agentname.plist.

GUI tools: LaunchControl and LaunchRocket

The launchctl interface is functional but not ergonomic for managing many agents. LaunchControl is the most capable GUI — it shows all agents across all directories, displays their status, lets you enable/disable, edit plists, and see recent log output. LaunchRocket is a simpler menu bar tool focused on personal agents. Either is worth installing if you write or manage more than a handful of custom launch agents.

Identifying slow-login culprits

If your login is slow, launch agents are frequently the cause. The approach:

1
2
3
4
5
6
7
8
# Look at all loaded agents sorted by label
launchctl list | sort -k3

# Check the boot timing (requires sudo)
sudo fs_usage -w -t 10 launchd

# Or use the built-in diagnostic report
sysdiagnose -f ~/Desktop -u

More practically, open Activity Monitor immediately after login, sort by CPU, and watch for any process spiking. Cross-reference with launchctl list to identify its plist. If it is a third-party application’s helper that you do not actively use, disable the application’s background mode in System Settings > General > Login Items first.


Putting It Together

The real power user setup on macOS is not any single tool — it is the accumulation of small frictions removed and small capabilities gained until the machine feels like an extension of thought rather than a thing you operate.

Quick Look plugins mean you almost never open an application just to inspect a file. Hammerspoon means your window layout is a keypress away, not a drag-and-drop exercise. Clipboard history means you stop treating copy-paste as a one-slot register. The defaults write changes mean your machine stops doing things you did not ask for (auto-hiding the dock with a delay, writing .DS_Store files everywhere). The CLI tools mean your terminal sessions interact fluidly with the rest of the system. Homebrew’s modern replacements mean the tools you use dozens of times per day are faster and more informative. And launch agents mean you can automate anything that should happen automatically, with full control over when and why.

None of this requires learning a new operating system philosophy. macOS rewards investment. The surface area of what is configurable and automatable is genuinely large, and every layer you peel back reveals more. The tools covered here are a starting point — a solid foundation that makes the next layer of exploration possible.

Comments