A powerful command-line tool for macOS that enables programmatic interaction with application menu items. Automate menu clicks, check menu item existence, and integrate menu actions into your shell scripts and workflows.
Perfect for automation scripts, CI/CD pipelines, and power users who want to control macOS applications from the terminal without visual interruption.
- π List & Discover - Enumerate all menu items for any running application
- π±οΈ Click & Execute - Programmatically trigger menu items from command line
- β Check & Validate - Verify menu item existence before attempting actions
- π³ Nested Navigation - Traverse complex menu hierarchies with simple syntax
- π Silent Execution - Non-visual mode for background automation (
--no-visual) - π€ Smart Typography - Automatic handling of ellipsis (
...ββ¦) and other characters - β‘ Multiple Methods - Various execution strategies (pick, keyboard shortcuts, selectors)
- ποΈ Modern App Support - Works with Electron, SwiftUI, and traditional AppKit applications
- π Optimized Performance - Script caching reduces execution time by 30-50%
- π¦ Batch Operations - Execute multiple menu items in a single operation (60-80% faster)
- β±οΈ Configurable Timing - Fine-tune delays for your specific needs
- π Async Support - Non-blocking operations for GUI integrations
- π― Alternative Names - Handle menu items that change names (Show/Hide, Start/Stop) (v2.2.0)
- π Fallback Chains - Try multiple menu paths until one works (v2.2.0)
- π State Detection - Check if menu items are enabled/disabled/checked (v2.2.0)
- π§ Smart Toggles - Automatically handle toggle menu items (v2.2.0)
- Installation
- Quick Start
- Performance β¨
- Usage
- Alternative Menu Names π― NEW
- State Detection & Toggles π§ NEW
- Examples
- Advanced Features
- Troubleshooting
- Contributing
- License
git clone https://github.com/yourusername/menucli.git
cd menucli
./build.sh
sudo cp .build/release/menu /usr/local/bin/Download the latest release from GitHub Releases and place it in your PATH.
MenuCLI requires accessibility permissions to interact with application menus:
- System Preferences β Security & Privacy β Privacy β Accessibility
- Click the π to make changes
- Add Terminal (or your terminal app) to the allowed applications
- Restart your terminal
# List all menu items in Safari
menu list Safari --recursive
# Click a menu item
menu click Safari "File > New Window"
# Silent execution (no visual menus)
menu click Safari "File > New Window" --no-visual
# Check if menu item exists (for scripting)
menu check Safari "File > New Window" --quiet && echo "Menu exists!"
# NEW: Execute multiple menu items in batch (v2.1.0+)
menu batch Safari "Edit > Select All;Edit > Copy;Edit > Paste"
# NEW: Fine-tune performance with custom delay (v2.1.0)
menu click Safari "File > New Window" --delay 20
# NEW: Handle alternative menu names (v2.2.0)
menu click-alt Safari "View > Show Sidebar|Hide Sidebar"
# NEW: Smart toggle for state-changing menu items (v2.2.0)
menu toggle Finder "View > Show Path Bar"
# NEW: Check menu item state (v2.2.0)
menu state Safari "File > Save" # Output: enabled, checked, has submenuMenuCLI v2.1.0 introduces significant performance improvements through intelligent caching and optimized execution:
| Operation Type | Before | After | Improvement |
|---|---|---|---|
| Single menu click | 350-500ms | 150-250ms | 43-50% faster |
| Repeated operations | 350-500ms each | 100-150ms | 70% faster (cached) |
| Multiple operations | 350-500ms each | 200-300ms total | 60-80% faster (batch) |
- Script Caching: Compiled AppleScripts are cached and reused
- Reduced Delays: Menu wait times reduced from 200-300ms to 50ms
- Batch Processing: Multiple operations in a single script execution
- Optimized String Building: Pre-allocated buffers for script generation
# Use caching (default - 30-50% faster for repeated operations)
menu click Safari "File > New Window"
# Disable caching for debugging
menu click Safari "File > New Window" --no-cache
# Custom delay timing (milliseconds)
menu click Safari "File > New Window" --delay 20 # 20ms delay
# Batch operations (60-80% faster than individual clicks)
menu batch Safari "Edit > Select All;Edit > Copy;Edit > Paste"# List top-level menu items
menu list Safari
# List all items recursively (including submenus)
menu list Safari --recursive
# Find specific menu structure
menu list "IntelliJ IDEA" --recursive | grep -i "run"# Basic menu clicking
menu click Safari "File > New Window"
menu click Finder "View > Show Path Bar"
menu click "Visual Studio Code" "Code > Preferences > Settings"
# Non-visual execution (recommended for automation)
menu click Safari "File > New Window" --no-visual
menu click "IntelliJ IDEA" "Run > Edit Configurations..." --no-visual
# With verbose output for debugging
menu click Safari "History > Clear History..." --verbose
# Try all execution methods until one succeeds
menu click Safari "File > New Window" --try-all
# Performance tuning (v2.1.0+)
menu click Safari "File > New Window" --delay 20 # Custom 20ms delay
menu click Safari "File > New Window" --no-cache # Disable caching for debugExecute multiple menu items in a single operation for significantly improved performance:
# Basic batch operation
menu batch Safari "Edit > Select All;Edit > Copy;Edit > Paste"
# With custom delay between operations
menu batch TextEdit "Format > Font > Bold;Format > Font > Italic" --delay 30
# Verbose output to see each operation
menu batch Finder "View > as List;View > Show Path Bar;View > Show Status Bar" --verbose
# Complex workflow automation
menu batch "Visual Studio Code" "File > Save All;View > Terminal;Terminal > Run Task"# Check if menu item exists
menu check Safari "File > New Window"
# Quiet mode (exit code only - perfect for scripts)
menu check Safari "File > New Window" --quiet
# With verbose debugging
menu check "IntelliJ IDEA" "Run > Manage Targets..." --verboseHandle menu items that change based on state with smart alternatives:
# Use pipe (|) for alternatives within the same menu level
menu click-alt Safari "View > Show Sidebar|Hide Sidebar"
menu click-alt TextEdit "Format > Make Plain Text|Make Rich Text"
menu click-alt Zoom "Meeting > Mute Audio|Unmute Audio"
# Use double pipe (||) for complete fallback paths
menu click-alt App "File > Save As..." || "File > Export..." || "File > Download"
menu click-alt IDE "Run > Run Tests" || "Test > Run All" || "Build > Test"
# Wait for menu item to become enabled (with timeout)
menu click-alt Safari "File > Save" --wait-enabled --timeout 3000
# Verbose mode shows which alternative was used
menu click-alt Safari "View > Show Sidebar|Hide Sidebar" --verboseCheck menu item states and handle toggles intelligently:
# Check menu item state
menu state Safari "File > Save" # Output: enabled
menu state TextEdit "Edit > Undo" # Output: disabled
menu state Finder "View > Show Path Bar" # Output: enabled, checked
# Smart toggle - automatically handles Show/Hide variations
menu toggle Safari "View > Show Status Bar"
menu toggle Finder "View > Show Path Bar"
menu toggle Zoom "Meeting > Mute Audio"
# Works with any toggle pattern:
# Show/Hide, Enable/Disable, Start/Stop, Mute/Unmute,
# Play/Pause, Connect/Disconnect, Lock/Unlock, etc.
# Verbose mode for debugging
menu toggle Safari "View > Show Sidebar" --verbose
menu state Safari "View > Show Sidebar" --verbose#!/bin/bash
# automated-safari-session.sh
APP="Safari"
# Check if Safari is running and has New Window option
if menu check "$APP" "File > New Window" --quiet; then
echo "π Opening new Safari window..."
menu click "$APP" "File > New Window" --no-visual
# Wait a moment then open preferences
sleep 1
if menu check "$APP" "Safari > Preferences..." --quiet; then
echo "βοΈ Opening Safari preferences..."
menu click "$APP" "Safari > Preferences..." --no-visual
fi
else
echo "β Safari not running or menu unavailable"
exit 1
fi#!/bin/bash
# toggle-dark-mode.sh
APP="System Preferences"
MENU_PATH="View > Dark Mode"
if menu check "$APP" "$MENU_PATH" --quiet; then
menu click "$APP" "$MENU_PATH" --no-visual
echo "π Dark mode toggled"
else
echo "π
Dark mode option not available"
fi#!/bin/bash
# setup-dev-environment.sh
APPS=(
"Visual Studio Code:Code > Preferences > Settings"
"IntelliJ IDEA:IntelliJ IDEA > Preferences..."
"Docker Desktop:Docker Desktop > Settings"
)
for app_menu in "${APPS[@]}"; do
IFS=':' read -r app menu <<< "$app_menu"
if menu check "$app" "$menu" --quiet; then
echo "π± Configuring $app..."
menu click "$app" "$menu" --no-visual
sleep 2 # Allow time for settings to open
else
echo "β οΈ $app not available"
fi
done#!/bin/bash
# fast-text-formatting.sh
APP="TextEdit"
# Old way: Multiple individual commands (slower)
# menu click "$APP" "Format > Font > Bold"
# menu click "$APP" "Format > Font > Italic"
# menu click "$APP" "Format > Font > Bigger"
# New way: Batch operation (60-80% faster!)
menu batch "$APP" "Format > Font > Bold;Format > Font > Italic;Format > Font > Bigger" --delay 20
# Performance comparison
echo "β‘ Testing performance improvement..."
# Test with caching (default)
START=$(date +%s%N)
menu click "$APP" "Edit > Select All"
menu click "$APP" "Edit > Select All" # Cached - much faster!
menu click "$APP" "Edit > Select All" # Cached - much faster!
END=$(date +%s%N)
echo "With cache: $((($END - $START) / 1000000))ms"
# Test without caching
START=$(date +%s%N)
menu click "$APP" "Edit > Select All" --no-cache
menu click "$APP" "Edit > Select All" --no-cache
menu click "$APP" "Edit > Select All" --no-cache
END=$(date +%s%N)
echo "Without cache: $((($END - $START) / 1000000))ms"#!/bin/bash
# robust-sidebar-toggle.sh
APP="Safari"
# Smart toggle that works regardless of current state
menu toggle "$APP" "View > Show Sidebar"
# Or use alternatives explicitly
menu click-alt "$APP" "View > Show Sidebar|Hide Sidebar"
# Handle different app versions with fallback chains
menu click-alt "$APP" "Safari > Settings..." || "Safari > Preferences..."
# Wait for save to be enabled before clicking
if menu click-alt "$APP" "File > Save" --wait-enabled --timeout 5000; then
echo "β
Document saved"
else
echo "β οΈ Save not available"
fi
# Check state before performing action
STATE=$(menu state "$APP" "Edit > Undo")
if [[ "$STATE" == *"enabled"* ]]; then
menu click "$APP" "Edit > Undo"
echo "β
Undo performed"
else
echo "β οΈ Nothing to undo"
fiMenuCLI uses > to separate menu levels:
"File > Save"- Click Save in File menu"Edit > Find > Find and Replace..."- Navigate nested menus"View > Show > Hidden Files"- Deep menu navigation
The tool automatically handles macOS typography:
...ββ¦(ellipsis conversion)'β'(smart quotes)--ββ(em dash)
# Specific execution method
menu click Safari "File > New Window" --method pick
menu click Safari "File > New Window" --method keyboard
menu click Safari "File > New Window" --method selector
# Try all methods until one works
menu click Safari "File > New Window" --try-all# Verbose output shows detailed execution flow
menu click Safari "File > New Window" --verbose
# Diagnose all execution methods for a menu item
menu diagnose Safari "File > New Window"MenuCLI automatically caches compiled AppleScripts for improved performance:
- Automatic: Caching is enabled by default
- Smart Invalidation: Cache clears on errors
- Thread-Safe: Concurrent access supported
- Debugging: Use
--no-cacheto disable
Fine-tune timing for your specific needs:
# Default: 50ms delay
menu click Safari "File > New Window"
# Fast: 20ms delay (for modern machines)
menu click Safari "File > New Window" --delay 20
# Slow: 100ms delay (for compatibility)
menu click Safari "File > New Window" --delay 100
# Batch operations with custom delay
menu batch Safari "Edit > Copy;Edit > Paste" --delay 30The underlying AppleScriptBridge supports async operations:
clickMenuItemAsync()- Non-blocking menu clicksclickMenuItemsBatchAsync()- Async batch operations- Completion handlers for result processing
- Ideal for GUI application integration
MenuCLI follows standard Unix exit codes:
0- Success1- Failure (menu not found, app not running, no permissions)
Perfect for shell scripting:
if menu check Safari "File > New Window" --quiet; then
echo "Menu available"
else
echo "Menu not available" >&2
exit 1
fi"This tool requires accessibility permissions"
- Grant accessibility permissions in System Preferences
- Ensure your terminal app is in the allowed list
- Restart terminal after granting permissions
"Application not found or not running"
- Verify the application is actually running
- Use exact application name as shown in menu bar
- Try
menu listwithout arguments to see available apps
"Menu item not found"
- Use
menu list AppName --recursiveto explore menu structure - Check capitalization and spacing (case-sensitive)
- Verify menu item is enabled (disabled items may not be accessible)
- Remember:
...automatically converts toβ¦
For better performance in automation scripts:
- Use
--no-visualflag to avoid menu animations - Use
--quietflag for cleaner script output - Cache menu existence checks when possible
Slow execution?
- Ensure caching is enabled (default) - avoid
--no-cacheunless debugging - Use batch operations for multiple menu clicks
- Reduce delays with
--delay 20for faster execution - First operation is always slower (cold cache), subsequent ones are faster
Cache issues?
- Use
--no-cacheto bypass caching when debugging - Cache automatically clears on errors
- Cache is per-session (not persistent across runs)
Batch operations failing?
- Ensure semicolon separation:
"Menu1 > Item1;Menu2 > Item2" - Test each operation individually first
- Use
--verboseto see which operations succeed/fail - Add custom delays between operations with
--delayif needed
Alternatives not working?
- Use single pipe
|for alternatives:"Show Sidebar|Hide Sidebar" - Use double pipe
||for fallback paths (requires shell escaping) - Test with
--verboseto see which alternative is being tried - Check exact menu item text with
menu list AppName --recursive
Toggle not detecting state?
- Ensure menu item follows standard patterns (Show/Hide, Enable/Disable, etc.)
- Use
menu stateto check current state first - Fall back to
click-altwith explicit alternatives if needed
State detection issues?
- Some apps don't properly report menu item states
- Try using AppleScript methods with
--verbosefor debugging - Check if menu item has a checkmark or is disabled visually
- β macOS 15.0+ (Sequoia)
- β macOS 14.0+ (Sonoma)
- β macOS 13.0+ (Ventura)
- β macOS 12.0+ (Monterey)
- β macOS 11.0+ (Big Sur)
- β macOS 10.15+ (Catalina)
- β Native AppKit applications
- β SwiftUI applications
- β Electron applications (VS Code, Discord, etc.)
- β Java applications (IntelliJ IDEA, etc.)
- β Web-based applications
- β iOS applications running on Apple Silicon
MenuCLI leverages macOS's built-in accessibility infrastructure:
- AXUIElement API - Direct access to application UI elements
- System Events - AppleScript bridge for guaranteed compatibility
- CGEvent - Low-level event simulation for keyboard shortcuts
- NSWorkspace - Application discovery and management
This multi-layered approach ensures compatibility across different application types and macOS versions.
We welcome contributions! Please see CONTRIBUTING.md for guidelines.
git clone https://github.com/yourusername/menucli.git
cd menucli
swift build
./test.sh- Use our issue templates
- Include macOS version and application details
- Provide menu path that's causing issues
- Run with
--verboseflag and include output
MIT License - see LICENSE file for details.
- Keyboard Maestro - GUI-based macro automation
- Alfred - Launcher with workflow support
- Raycast - Modern launcher with extensions
- cliclick - General mouse/keyboard CLI tool
- Hammerspoon - Lua-based automation framework
MenuCLI focuses specifically on menu automation via command line, making it ideal for shell scripts, CI/CD pipelines, and terminal-based workflows.
β Star this repo if you find it useful!
Report Bug β’ Request Feature β’ Contribute