Skip to content

macOS: App Intents#7634

Merged
mitchellh merged 27 commits intomainfrom
macos-intents
Jun 21, 2025
Merged

macOS: App Intents#7634
mitchellh merged 27 commits intomainfrom
macos-intents

Conversation

@mitchellh
Copy link
Copy Markdown
Contributor

@mitchellh mitchellh commented Jun 20, 2025

This PR integrates Ghostty on macOS with the App Intents system. The focus of this initial work was on enabling Apple Shortcuts on macOS, but App Intents are the same underlying system that powers a number of other Apple features such as Spotlight, Siri, Widgets, and more. We don't do much with these latter ones yet, though.

Additionally, this PR begins to refactor and untangle some of our libghostty API calls from macOS views. Presently, macOS views and view controllers directly call into the libghostty API and own libghostty data models. This tight coupling is kind of nasty because it tends to also couple libghostty API calls to the main GUI thread when they don't really have to be (they just have to not be concurrently accessed). This becomes an issue because App Intents run on background threads. This PR starts to extract out some of this business logic into standalone classes, but we still force all execution onto the main thread during the transition.

Version requirement: Most of the shortcuts will work on macOS 13, but there are some that require macOS 14, and some functionality will require macOS 26. We gracefully degrade in all scenarios (the capabilities that are unavailable just don't show up on older systems).

Important

This bumps our build requirements on macOS to Xcode 26 and the macOS 26 SDK. You can still build on macOS 15, but you must be using the Xcode 26 beta. This includes a README update about that.

Why?

Apple Shortcuts is an extremely powerful scripting tool on Apple platforms. It comes with a number of built-in capabilities such as moving windows, taking screenshots, fetching secrets, and more. By integrating with Apple Shortcuts, it allows Ghostty to become scriptable to a certain extent while also being able to take advantage of this large ecosystem.

It is a huge downside that Shortcuts is Apple-only and I still would like to make Ghostty scriptable to some extent on Linux and other platforms as well. This work doesn't preclude that goal, but gives us an answer to a large subset of users (macOS users), which is great.

Beyond this, no terminals integrate with Apple Shortcuts except the built-in Terminal. And even then, the built-in Terminal only exposes two actions (run script and run script over SSH). I think there's a lot that can be done by exposing more functionality and I'm excited to see what people do with this.

Finally, I think Shortcuts is possibly a way we can do some GUI testing on macOS. That remains to be explored but it seems promising.

Capability

The initial set of Shortcut actions is shown in the screenshot below:

CleanShot 2025-06-20 at 12 19 47@2x

These can be combined with the built-in shortcuts to do some pretty interesting things.

Future

There are more capabilities I'd like to expose, but they require changing core parts of Ghostty that I didn't want to mix into this PR.

Security

Scripting Ghostty can be considered a security risk, since it allows arbitrary command execution, reading terminal output, etc. Therefore, Ghostty will ask for permission prior to allowing any Shortcut to remote control it:

image

This can be directly overridden using the new macos-shortcuts configuration, which defaults to ask but can also be set to deny or allow with self-explanatory behaviors.

@mitchellh mitchellh requested a review from a team as a code owner June 20, 2025 19:24
Copy link
Copy Markdown
Contributor

@jparise jparise left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great!

IntentDescription has an optional categoryName argument that the Shortcuts UI will use to categorize (+ group) intents. There's also searchKeywords, which I haven't used before.

Not sure if you looked at these but might be useful given how many we offer.

Comment on lines +99 to +114
func entities(for identifiers: [TerminalEntity.ID]) async throws -> [TerminalEntity] {
return all.filter {
identifiers.contains($0.uuid)
}.map {
TerminalEntity($0)
}
}

@MainActor
func entities(matching string: String) async throws -> [TerminalEntity] {
return all.filter {
$0.title.localizedCaseInsensitiveContains(string)
}.map {
TerminalEntity($0)
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think both of these cases could be slightly improved using either a lazy collection (all.lazy.filter.map) or compactMap to avoid extra iterations.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's never going to be so many terminals that this will ever matter. Even if there were 1,000 terminals it'd be fast, so I don't think we need to worry about it...

Comment thread macos/Sources/Features/App Intents/Entities/TerminalEntity.swift Outdated
Comment thread macos/Sources/Features/App Intents/NewTerminalIntent.swift
Comment thread macos/Sources/Features/App Intents/GhosttyIntentError.swift Outdated
Comment thread macos/Sources/Helpers/Extensions/Array+Extension.swift Outdated
@mitchellh
Copy link
Copy Markdown
Contributor Author

IntentDescription has an optional categoryName argument that the Shortcuts UI will use to categorize (+ group) intents. There's also searchKeywords, which I haven't used before.

Both sound good but not sure what I'd put as the category. Search keywords we can also continue to think of.

@mitchellh
Copy link
Copy Markdown
Contributor Author

Based on various feedback, I modified the ask behavior to ask once and remember forever by default. This is more in line with how Apple permission dialogs work. Additionally, many people suggested that Shortcuts itself is responsible for protecting users from malicious scripts so I shouldn't bear this upon myself. I think asking once is a fair line between these two.

@mitchellh mitchellh merged commit 6fe72db into main Jun 21, 2025
74 checks passed
@mitchellh mitchellh deleted the macos-intents branch June 21, 2025 14:07
@github-actions github-actions Bot added this to the 1.2.0 milestone Jun 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants