How cmux Integrates with AppleScript and System Automation
TLDR: cmux exposes a comprehensive AppleScript API through Swift scripting objects, allowing macOS automation tools to programmatically create windows, manage tabs, split panes, and inject text into terminals.
The manaflow-ai/cmux terminal multiplexer provides deep system automation capabilities through native AppleScript integration. By implementing scriptable objects in Swift, cmux allows macOS automation tools like Shortcuts and Keyboard Maestro to control terminal sessions without launching external binaries. This integration relies on three core architectural components that bridge the gap between AppleScript commands and the application's UI state.
Architecture Overview
The cmux AppleScript integration rests on three Swift components that expose the terminal multiplexer’s internal state to macOS automation frameworks.
NSApplication Extension
In Sources/AppleScriptSupport.swift (lines 81‑115), the NSApplication extension provides global scripting entry points such as scriptWindows, terminals, and validateScript. This extension registers custom script commands and acts as the primary bridge that macOS uses when AppleScript targets the cmux application.
AppDelegate Helpers
The AppDelegate.swift file (lines 4432‑4476) maintains the list of scriptable windows, tabs, and terminals through helper methods including scriptableMainWindows(), scriptableMainWindow(windowId:), and scriptableMainWindowForTab(_:). These functions supply the concrete model objects that back the scripting layer’s object graph.
Scripting Objects
The ScriptWindow, ScriptTab, and ScriptTerminal classes — defined in Sources/AppleScriptSupport.swift (lines 120‑290) — are NSObject subclasses that map directly to cmux UI elements. These classes implement NSScriptCommand selectors such as handleActivateWindowCommand:, handleCloseCommand:, and handlePerformActionScriptCommand:, exposing properties like id, title, and terminals as AppleScript-compatible selectors.
How the Scripting Bridge Works
The integration follows a six-step architecture that translates AppleScript instructions into UI actions.
1. Enabling the Bridge
The NSApplication.isAppleScriptEnabled property always returns true (lines 84‑90), ensuring the scripting bridge is active. This flag gates every scriptable property and command in the application.
2. Discoverable Object Hierarchies
The scriptWindows property returns an array of ScriptWindow objects gathered from AppDelegate.shared?.scriptableMainWindows() (line 106). The terminals property flattens all terminals across every window and tab (lines 129‑138).
3. Object Specifiers
Each scripting class overrides objectSpecifier to provide a unique NSUniqueIDSpecifier keyed by the underlying model object’s UUID. This allows AppleScript to resolve objects via selectors like valueInScriptWindowsWithUniqueID: (lines 194‑202, 236‑244).
4. Command Handling
Custom NSScriptCommand subclasses implement the actual work:
handleNewWindowScriptCommand:creates a new cmux window viaAppDelegate.shared?.createMainWindow()(line 182)handleNewTabScriptCommand:adds a new workspace (tab) to a specified or frontmost windowhandlePerformActionScriptCommand:forwards string actions (such as Ghostty bindings) to a terminalhandleSplitCommand:performs pane splits within a terminalhandleFocusCommand:brings a terminal to the foregroundScriptInputTextCommandreads the direct parameter as text and sends it to the target terminal (lines 662‑686)
5. Error Handling
When scripts request invalid objects or missing parameters, the code populates command.scriptErrorNumber and command.scriptErrorString using localized strings from AppleScriptStrings (lines 94‑99, 165‑172).
6. Security and Focus Policy
All script commands first call NSApp.validateScript(command:) to verify AppleScript is enabled. Commands that could steal focus, such as handleActivateWindowCommand:, explicitly check focusScriptableMainWindow(..., bringToFront: true) before executing.
Practical AppleScript Examples
Creating Windows and Typing Commands
tell application "cmux"
set newWin to make new script window
set termList to terminals of newWin
set firstTerm to item 1 of termList
input text "ls -la" to firstTerm
end tell
This script invokes NSApplication.handleNewWindowScriptCommand: (line 182), retrieves terminals via NSApplication.terminals (lines 129‑138), and executes ScriptInputTextCommand (lines 662‑686) to inject text.
Splitting Terminal Panes
tell application "cmux"
set frontTerm to first terminal of front window
split frontTerm direction "GSdn"
end tell
The split command resolves the terminal via valueInTerminalsWithUniqueID: (line 206), then maps "GSdn" to SplitDirection.down using ScriptSplitDirection (lines 90‑104) before executing ScriptTerminal.handleSplitCommand: (lines 620‑665).
Focusing Tabs by UUID
set targetID to "A1B2C3D4-E5F6-7890-1234-56789ABCDEF0"
tell application "cmux"
set targetTab to value in tabs with unique ID targetID
select targetTab
end tell
This resolves the tab via ScriptWindow.valueInTabs(uniqueID:) (lines 296‑304), then invokes ScriptTab.handleSelectTabCommand: (lines 446‑459) to activate it.
Closing Windows
tell application "cmux"
close front window
end tell
The close command triggers ScriptWindow.handleCloseWindowCommand: (lines 334‑352) to dismiss the frontmost window.
Key Implementation Files
| File | Purpose | Key Lines |
|---|---|---|
Sources/AppleScriptSupport.swift |
Scripting objects, command handlers, error strings, and NSApplication extension |
81‑115, 120‑290, 662‑686 |
Sources/AppDelegate.swift |
Helper methods mapping UI state to scriptable objects | 4432‑4476 |
Sources/TerminalController.swift |
Internal usage of scriptableMainWindows() for command broadcasting |
~5154 |
Resources/Info.plist |
Declares AppleScript terminology and class names (CmuxScriptWindow, CmuxScriptTab, CmuxScriptTerminal) for runtime resolution |
N/A |
Summary
- cmux implements three core Swift components — an
NSApplicationextension,AppDelegatehelpers, and scripting objects — to expose its UI to AppleScript. - Object specifiers use UUID-based
NSUniqueIDSpecifierinstances to ensure reliable script-to-object resolution. - Command handlers translate high-level AppleScript commands like
make new script windoworsplitinto concrete cmux actions. - Security checks via
validateScript:and focus policies prevent unauthorized automation actions. - The integration supports Shortcuts, Keyboard Maestro, and any macOS tool that drives scriptable applications.
Frequently Asked Questions
How do I enable AppleScript support for cmux?
AppleScript support is always enabled in cmux. The NSApplication.isAppleScriptEnabled property returns true by default (lines 84‑90 in AppleScriptSupport.swift), meaning the application immediately responds to AppleScript commands without requiring additional configuration or preferences changes.
Which cmux objects can I control via AppleScript?
You can control three primary object types: ScriptWindow (top-level windows), ScriptTab (workspace tabs), and ScriptTerminal (individual terminal panes). Each exposes properties like id, title, and collections of child objects (e.g., terminals of newWin), allowing you to navigate the entire window hierarchy programmatically.
How does cmux handle AppleScript errors?
When a script provides invalid parameters or requests non-existent objects, cmux populates command.scriptErrorNumber and command.scriptErrorString with localized error descriptions from the AppleScriptStrings table (lines 94‑99 and 165‑172). These error messages surface directly in the macOS Script Editor or calling automation tool, providing clear feedback about what went wrong.
Can I use the macOS Shortcuts app with cmux?
Yes. Because cmux exposes a standard AppleScript dictionary, you can use the Run AppleScript action in Shortcuts, or any automation tool like Keyboard Maestro, Hammerspoon, or Alfred, to drive cmux. The validateScript: security check ensures that only authorized scripts can execute focus-stealing commands, making the integration safe for automated workflows.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →