Setting up a new Mac or provisioning developer workstations often involves changing dozens of default application handlers. By default, macOS opens source code files, plain text documents, markdown notes, and video media in Apple’s built-in utilities like TextEdit and QuickTime Player rather than modern development tools like Visual Studio Code, Cursor, or VLC. While macOS lets you manually change default handlers via Finder’s “Get Info” pane, repeating this process for every file extension (.js, .ts, .py, .md, .json) is tedious. The open-source duti command-line utility enables complete automation of default application associations via shell scripts and configuration files.
What Is duti and How Does It Function?
The duti utility (Default Uniform Type Identifier) interfaces directly with the macOS Launch Services framework. Key characteristics include:
- UTI and Extension Mapping: Can set default applications based on standard file extensions (such as
.pdfor.html) or Apple Uniform Type Identifiers (UTIs likepublic.plain-textorpublic.json). - Bundle Identifier Association: Uses reverse-DNS application bundle IDs (e.g.
com.microsoft.VSCode) to link target applications unambiguously. - URL Protocol Scheme Handling: Sets default handlers for custom web protocols like
mailto:,http:,ssh:, andftp:. - Scriptable Provisioning: Allows you to store your default application settings in a simple text configuration file for automated dotfile installations.
Step 1: Install duti on macOS
The most straightforward method to install duti is via Homebrew:
brew install duti
Verify that the binary is installed and operational:
duti -V
Step 2: Find Application Bundle Identifiers
To assign an application using duti, you must identify its unique macOS bundle identifier. Run osascript or mdls to query installed applications:
Find Bundle ID via AppleScript
# Find bundle ID for Visual Studio Code
osascript -e 'id of app "Visual Studio Code"'
# Returns: com.microsoft.VSCode
# Find bundle ID for VLC Player
osascript -e 'id of app "VLC"'
# Returns: org.videolan.vlc
# Find bundle ID for Google Chrome
osascript -e 'id of app "Google Chrome"'
# Returns: com.google.Chrome
Step 3: Find Uniform Type Identifiers (UTIs) for File Types
To determine the exact UTI assigned to a specific file on your system, use the native mdls (Metadata List) command:
mdls -name kMDItemContentType -name kMDItemContentTypeTree script.py
Terminal output will display the primary content type (e.g. public.python-script) and its parent hierarchy.
Step 4: Associate File Extensions and UTIs with duti
The duti syntax follows a clear structure: duti -s <bundle_id> <extension_or_uti> <role>.
The role parameter defines application permissions:
all: Default application for opening, viewing, and editing.editor: Opens file for editing.viewer: Opens file strictly for reading/viewing.
Set Default Editor for Programming Extensions
# Set Visual Studio Code as default handler for code and text files
duti -s com.microsoft.VSCode .js all
duti -s com.microsoft.VSCode .ts all
duti -s com.microsoft.VSCode .py all
duti -s com.microsoft.VSCode .json all
duti -s com.microsoft.VSCode .md all
duti -s com.microsoft.VSCode .yaml all
Set Default Video Player
# Set VLC as default player for video files
duti -s org.videolan.vlc .mkv all
duti -s org.videolan.vlc .mp4 all
duti -s org.videolan.vlc .avi all
Set Default URL Protocol Handlers
# Set Google Chrome as default handler for web URLs
duti -s com.google.Chrome http
duti -s com.google.Chrome https
Step 5: Inspect Current Default Handlers
To check which application currently handles a particular extension or UTI:
# Check default handler for .py files
duti -x py
# Check default handler for a UTI
duti -d public.plain-text
The output reports the application name, bundle identifier, and full filesystem path.
Step 6: Automate Workstation Provisioning via a duti Configuration File
Rather than executing individual commands, save all your desired associations into a unified configuration file (e.g. ~/.duti):
# ~/.duti configuration template
# bundle_id uti_or_extension role
com.microsoft.VSCode .c all
com.microsoft.VSCode .cpp all
com.microsoft.VSCode .go all
com.microsoft.VSCode .rs all
com.microsoft.VSCode public.plain-text all
com.microsoft.VSCode public.json all
org.videolan.vlc .mp4 viewer
org.videolan.vlc .mkv viewer
org.videolan.vlc public.movie viewer
To apply all mappings across your operating system in one step, point duti to the configuration file:
duti ~/.duti
Launch Services updates immediately; no restart or Finder reload is required.
Resetting Launch Services Cache
If macOS occasionally fails to register a new handler after a major operating system update, rebuild the Launch Services database using the native lsregister tool:
/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister -kill -r -domain local -domain system -domain user
killall Finder
After the cache rebuilds, re-run duti ~/.duti to re-establish your preferred application handlers.