Skip to content

macos: add pid and tty properties to AppleScript terminal and App Intents TerminalEntity#11922

Open
TweedBeetle wants to merge 1 commit intoghostty-org:mainfrom
TweedBeetle:pid-tty-applescript
Open

macos: add pid and tty properties to AppleScript terminal and App Intents TerminalEntity#11922
TweedBeetle wants to merge 1 commit intoghostty-org:mainfrom
TweedBeetle:pid-tty-applescript

Conversation

@TweedBeetle
Copy link
Copy Markdown

Summary

Add pid and tty as read-only properties to:

  • The AppleScript terminal class
  • The App Intents TerminalEntity

This enables reliable process-to-terminal mapping for automation tools when multiple terminals share the same working directory.

Changes

  • macos/Ghostty.sdef: add pid and tty to the terminal class
  • macos/Sources/Features/AppleScript/ScriptTerminal.swift: add AppleScript getters
  • macos/Sources/Ghostty/Ghostty.Surface.swift: add Swift accessors for foreground PID and PTY name
  • src/apprt/embedded.zig: add C bridge exports for foreground PID and PTY name
  • include/ghostty.h: declare the new C API entry points
  • macos/Sources/Features/App Intents/Entities/TerminalEntity.swift: surface the same values in App Intents

Testing

  • zig build -Demit-macos-app=false
  • /opt/homebrew/bin/nu macos/build.nu --configuration Debug --action build
  • swiftlint lint 'macos/Sources/Features/AppleScript/ScriptTerminal.swift'
  • swiftlint lint 'macos/Sources/Features/App Intents/Entities/TerminalEntity.swift'
  • swiftlint lint 'macos/Sources/Ghostty/Ghostty.Surface.swift'
  • AppleScript smoke test against the built app:
    • get {id, pid, tty, working directory} of terminal 1
    • get pid of every terminal
    • get tty of every terminal
    • Verified returned pid/tty against ps -p <pid> -o pid=,tty=,comm= and lsof -a -p <pid> -d cwd -Fn

AI Disclosure

This PR was developed with AI assistance (Codex). The contributor understands the changes and can explain the implementation.

Closes #11592
Closes #10756

@TweedBeetle TweedBeetle requested a review from a team as a code owner March 28, 2026 11:55
@ghostty-bot ghostty-bot bot added the os/macos label Mar 28, 2026
Copy link
Copy Markdown
Member

@jcollie jcollie left a comment

Choose a reason for hiding this comment

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

Only commenting on the Zig code. The Swift code will need review by someone else.

}
};

// ghostty_string_s
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is partially duplicating source from main_c.zig. The String definition should be moved to a shared location (maybe src/c/string.zig?) and referenced from there.

ghostty/src/main_c.zig

Lines 62 to 102 in 0d1f77b

/// ghostty_string_s
pub const String = extern struct {
ptr: ?[*]const u8,
len: usize,
sentinel: bool,
pub const empty: String = .{
.ptr = null,
.len = 0,
.sentinel = false,
};
pub fn fromSlice(slice: anytype) String {
return .{
.ptr = slice.ptr,
.len = slice.len,
.sentinel = sentinel: {
const info = @typeInfo(@TypeOf(slice));
switch (info) {
.pointer => |p| {
if (p.size != .slice) @compileError("only slices supported");
if (p.child != u8) @compileError("only u8 slices supported");
const sentinel_ = p.sentinel();
if (sentinel_) |sentinel| if (sentinel != 0) @compileError("only 0 is supported for sentinels");
break :sentinel sentinel_ != null;
},
else => @compileError("only []const u8 and [:0]const u8"),
}
},
};
}
pub fn deinit(self: *const String) void {
const ptr = self.ptr orelse return;
if (self.sentinel) {
state.alloc.free(ptr[0..self.len :0]);
} else {
state.alloc.free(ptr[0..self.len]);
}
}
};

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.

We have a "standard" libghostty string type in src/lib/string.zig (we use for libghostty-vt). We should probably restandardize on that. Short of that though I agree with jcollie here.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Addressed in 82e66aa17. I extracted the C API String into src/c/string.zig for this PR and updated main_c.zig, embedded.zig, and config/CApi.zig to share it. Agreed that any broader libghostty string restandardization would be follow-up work.

/// freed by the caller via ghostty_string_free.
export fn ghostty_surface_tty_name(surface: *Surface) String {
const tty_name = surface.core_surface.getProcessInfo(.tty_name) orelse return .empty;
const copy = global_state.alloc.dupeZ(u8, tty_name) catch |err| {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The allocator should be available from surface.app.core_app.alloc thus making the import of global.zig above unnecessary.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Addressed in 82e66aa17. ghostty_surface_tty_name now allocates via surface.app.core_app.alloc, and the extra global_state import in embedded.zig is gone.

Copy link
Copy Markdown
Contributor

@mitchellh mitchellh left a comment

Choose a reason for hiding this comment

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

Swift side looks fine, agree on the Zig review from @jcollie

}
};

// ghostty_string_s
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.

We have a "standard" libghostty string type in src/lib/string.zig (we use for libghostty-vt). We should probably restandardize on that. Short of that though I agree with jcollie here.

@TweedBeetle TweedBeetle force-pushed the pid-tty-applescript branch from 0fde537 to 82e66aa Compare March 28, 2026 16:28
src/c/string.zig Outdated
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.

Sorry actually we shouldn't create a whole new c directory for this. This should live somewhere else but I'm not sure where yet. Let me think about it.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Ok, no problem 👍

coryszatkowski added a commit to coryszatkowski/ClawDeck that referenced this pull request Mar 29, 2026
Ghostty's AppleScript API exposes working directory but not TTY.
We cross-reference window CWDs against child shell process CWDs
to resolve TTYs indirectly. Windows with duplicate CWDs get tiling
but no status colors. Will switch to direct tty property once
Ghostty ships it (ghostty-org/ghostty#11922).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
coryszatkowski added a commit to coryszatkowski/ClawDeck that referenced this pull request Mar 29, 2026
* feat: add Ghostty to TERMINAL_APPS set

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: add Ghostty TTY resolution via CWD matching

Ghostty's AppleScript API exposes working directory but not TTY.
We cross-reference window CWDs against child shell process CWDs
to resolve TTYs indirectly. Windows with duplicate CWDs get tiling
but no status colors. Will switch to direct tty property once
Ghostty ships it (ghostty-org/ghostty#11922).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: skip windows with unresolved TTY in tty map building

Ghostty windows can have tty=None when CWD matching fails (e.g. duplicate
CWDs). Guard against None TTYs flowing into _resolve_tty_cwd by checking
info.get("tty") is truthy before storing in tty_map.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: use case-insensitive pgrep for Ghostty process name

On macOS, Ghostty's process name is "Ghostty" (capital G) when launched
from the .app bundle. Adding -i flag to pgrep ensures we find it
regardless of case.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: add Ghostty to supported terminals in README

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: fix Ghostty AppleScript and process tree walking

Two bugs found during live testing:
1. Ghostty windows don't have 'bounds' property — use System Events
   for position/size and Ghostty scripting API for working directory
2. ps -g doesn't find Ghostty child shells (different process groups) —
   use recursive pgrep -P to walk ghostty → login → shell tree

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: coryszatkowski <213997628+coryszatkowski@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@TweedBeetle
Copy link
Copy Markdown
Author

Happy to revert src/c/ and just import String from main_c.zig in embedded.zig if that's simpler for now. Or I can move it wherever you'd prefer. Just let me know 🫡

Copy link
Copy Markdown
Member

@bo2themax bo2themax left a comment

Choose a reason for hiding this comment

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

AppIntents/Shortcut.app builds their cache really weirdly, it took me some time to make it update to new entity, but it worked.

Image

@TweedBeetle
Copy link
Copy Markdown
Author

Hey @mitchellh, just wanted to check in on this. Happy to go whichever direction on the Zig file placement. As far as I understand, there are three options:

  1. Revert src/c/ and import String from main_c.zig in embedded.zig
  2. Move the file somewhere else you'd prefer
  3. Keep current approach

Let me know if you have thoughts. Everything else is approved 🫡

@TweedBeetle TweedBeetle force-pushed the pid-tty-applescript branch from 82e66aa to d5e7965 Compare April 8, 2026 08:55
@TweedBeetle
Copy link
Copy Markdown
Author

Went ahead with option 1 to unblock: reverted src/c/ and inlined the String type back into main_c.zig (force-pushed). Zig diff is now a no-op aside from the two new CAPI exports in embedded.zig. Happy to move the extraction elsewhere if you'd still prefer that, just let me know and I'll reshape it.

Expose the foreground process PID and TTY device path as read-only properties on the AppleScript terminal class and App Intents TerminalEntity. This enables reliable process-to-terminal mapping for automation tools when multiple terminals share the same CWD.

Closes ghostty-org#11592
Closes ghostty-org#10756

Session: 019d341c-a165-7843-a2f7-2f426114cf17
@TweedBeetle TweedBeetle force-pushed the pid-tty-applescript branch from d5e7965 to 58573a2 Compare April 9, 2026 17:53
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.

macOS: Add pid and tty properties to AppleScript terminal class macOS: Expose TTY, PID on TerminalEntity within App Shortcuts

4 participants