Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ _Eg. if you wanted cd to windows to be "Red Sands"_
defaults write name.tuley.jay.cd-to cdto-new-window-setting -string "Red Sands"
```

To support ghostty:

```bash
defaults write name.tuley.jay.cd-to cdto-terminal-app ghostty

Copilot AI Apr 16, 2026

Copy link

Choose a reason for hiding this comment

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

The README example writes cdto-terminal-app without specifying the type; other examples use -bool / -string. To avoid defaults inferring an unexpected type (and to be consistent), use -string "ghostty" in the command.

Suggested change
defaults write name.tuley.jay.cd-to cdto-terminal-app ghostty
defaults write name.tuley.jay.cd-to cdto-terminal-app -string "ghostty"

Copilot uses AI. Check for mistakes.
```




Expand Down Expand Up @@ -93,7 +99,7 @@ Version 2.0 (2005)
* Ported to objective-c using appscript, boosting launch & execution speed
* properly resolves aliases
* no longer shows icon in dock on launch

Version 1.0 (2003)
* targeted Panther OS X 10.3
* was applescript
Expand Down
170 changes: 119 additions & 51 deletions cd to .../cd to .../main.m
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#import <Cocoa/Cocoa.h>
#import <ScriptingBridge/ScriptingBridge.h>
#import <ApplicationServices/ApplicationServices.h>
Comment on lines 9 to +11

Copilot AI Apr 16, 2026

Copy link

Choose a reason for hiding this comment

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

#import <ApplicationServices/ApplicationServices.h> appears unused in this file. If nothing from ApplicationServices is needed for the Ghostty integration, please remove it to avoid unused-import warnings (or keep it only if required elsewhere).

Copilot uses AI. Check for mistakes.

#import "Finder.h"
#import "Terminal.h"
Expand All @@ -17,13 +18,116 @@ NSUInteger linesOfHistory(TerminalTab* tab) {
return [[hist componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]] count];
}

// Check if Ghostty is currently running
BOOL isGhosttyRunning(void) {
NSArray* runningApps = [[NSWorkspace sharedWorkspace] runningApplications];
for (NSRunningApplication* app in runningApps) {
if ([app.bundleIdentifier isEqualToString:@"com.mitchellh.ghostty"]) {
return YES;
}
}
return NO;
}

void openInGhostty(NSURL* url) {
NSLog(@"[cdto] Trying Ghostty...");
NSURL* appURL = [[NSWorkspace sharedWorkspace]
URLForApplicationWithBundleIdentifier:@"com.mitchellh.ghostty"];
if (appURL == nil) {
NSLog(@"[cdto] Ghostty app not found!");
return;
}
NSLog(@"[cdto] Found Ghostty at: %@", appURL);

NSString* workingDir = [url path];
NSLog(@"[cdto] Opening with working directory: %@", workingDir);
NSLog(@"[cdto] Ghostty running: %@", isGhosttyRunning() ? @"YES" : @"NO");

NSTask* task = [[NSTask alloc] init];
task.executableURL = [NSURL fileURLWithPath:@"/usr/bin/osascript"];
task.arguments = @[
@"-e", @"on run argv",
@"-e", @"set workingDir to item 1 of argv",
@"-e", @"tell application \"Ghostty\"",
@"-e", @"set cfg to new surface configuration",
@"-e", @"set initial working directory of cfg to workingDir",
@"-e", @"if it is running then",
@"-e", @"if (count of windows) > 0 then",
@"-e", @"new tab in front window with configuration cfg",
@"-e", @"else",
@"-e", @"new window with configuration cfg",
@"-e", @"end if",
@"-e", @"else",
@"-e", @"new window with configuration cfg",
@"-e", @"end if",
@"-e", @"activate",
@"-e", @"end tell",
@"-e", @"end run",
@"--", workingDir
];
[task launch];
[task waitUntilExit];
Comment on lines +46 to +69

Copilot AI Apr 16, 2026

Copy link

Choose a reason for hiding this comment

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

NSTask is configured via executableURL, but the code calls -launch. With executableURL set, the supported API is -launchAndReturnError: (or set launchPath instead). As written this can raise an exception at runtime (or fail to start the task) and Ghostty won’t open.

Copilot uses AI. Check for mistakes.

if (task.terminationStatus == 0) {
NSLog(@"[cdto] Ghostty opened successfully");
} else {
NSLog(@"[cdto] Ghostty AppleScript failed with exit code: %d", task.terminationStatus);
}
}

void openInTerminal(NSURL* url) {
NSLog(@"[cdto] Trying Terminal...");
TerminalApplication* terminal = [SBApplication applicationWithBundleIdentifier:@"com.apple.Terminal"];

TerminalWindow* win = nil;
if ([[terminal windows] count] == 1){
win = [[terminal windows] objectAtLocation:@1];
win = [[terminal windows] objectWithID: [NSNumber numberWithInteger:win.id]];
}
[terminal open:@[url]];
TerminalWindow* newWin = [[terminal windows] objectAtLocation:@1];
newWin = [[terminal windows] objectWithID: [NSNumber numberWithInteger:newWin.id]];
TerminalTab* newTab = [[newWin tabs] objectAtLocation:@1];

NSString* setName = [[NSUserDefaults standardUserDefaults] stringForKey:@"cdto-new-window-setting"];
if(setName != nil && ![setName isEqualToString:@""]) {
TerminalSettingsSet* chosenSet = nil;
for (TerminalSettingsSet *set in [terminal settingsSets]) {
if([[set name] isEqualToString:setName]){
chosenSet = set;
}
}
if(chosenSet != nil){
newTab.currentSettings = chosenSet;
}
}

if([[NSUserDefaults standardUserDefaults] boolForKey:@"cdto-close-default-window"]){
if([[win tabs] count] == 1){
TerminalTab* tab = [[win tabs]objectAtLocation:@1];
if(![tab busy]){
NSUInteger oldTabLines = linesOfHistory(tab);
while([newTab busy]){
[NSThread sleepForTimeInterval:0.1f];
}
NSUInteger newTabLines = linesOfHistory(newTab);
if(oldTabLines == newTabLines){
[win closeSaving:TerminalSaveOptionsNo savingIn:nil];
}
}
}
}

[terminal activate];
}

int main(int argc, const char * argv[]) {
@autoreleasepool {
// Setup code that might create autoreleased objects goes here.
NSString* terminalApp = [[NSUserDefaults standardUserDefaults] stringForKey:@"cdto-terminal-app"];
NSLog(@"[cdto] cdto-terminal-app value: '%@'", terminalApp);

FinderApplication* finder = [SBApplication applicationWithBundleIdentifier:@"com.apple.Finder"];

TerminalApplication* terminal = [SBApplication applicationWithBundleIdentifier:@"com.apple.Terminal"];


FinderItem *target = [(NSArray*)[[finder selection] get] firstObject];
FinderFinderWindow* findWin = [[finder FinderWindows] objectAtLocation:@1];
findWin = [[finder FinderWindows] objectWithID:[NSNumber numberWithInteger: findWin.id]];
Expand All @@ -32,65 +136,29 @@ int main(int argc, const char * argv[]) {
target = [[findWin target] get];
selected = false;
}

NSDictionary* itemProperties = [target properties];
id originalItem = [itemProperties objectForKey:@"originalItem"];
if (originalItem != nil && originalItem != [NSNull null]){
target = originalItem;
}

NSString* fileUrl = [target URL];
NSLog(@"[cdto] fileUrl: %@", fileUrl);
if(fileUrl != nil && ![fileUrl hasSuffix:@"/"] && selected){
fileUrl = [fileUrl stringByDeletingLastPathComponent];
}

NSURL* url = [NSURL URLWithString:fileUrl];
NSLog(@"[cdto] url: %@", url);
if (url != nil){
TerminalWindow* win = nil;
if ([[terminal windows] count] == 1){
//get front most and then reference by id
win = [[terminal windows] objectAtLocation:@1];
win = [[terminal windows] objectWithID: [NSNumber numberWithInteger:win.id]];
}
[terminal open:@[url]];
//get front most and then reference by id
TerminalWindow* newWin = [[terminal windows] objectAtLocation:@1];
newWin = [[terminal windows] objectWithID: [NSNumber numberWithInteger:newWin.id]];
TerminalTab* newTab = [[newWin tabs] objectAtLocation:@1];

NSString* setName = [[NSUserDefaults standardUserDefaults] stringForKey:@"cdto-new-window-setting"];
if(setName != nil && ![setName isEqualToString:@""]) { //setting set
TerminalSettingsSet* chosenSet = nil;
for (TerminalSettingsSet *set in [terminal settingsSets]) {
if([[set name] isEqualToString:setName]){
chosenSet = set;
}
}
if(chosenSet != nil){
newTab.currentSettings = chosenSet;
}
}

if([[NSUserDefaults standardUserDefaults] boolForKey:@"cdto-close-default-window"]){ //close first launch window
if([[win tabs] count] == 1){
TerminalTab* tab = [[win tabs]objectAtLocation:@1];
if(![tab busy]){
//if history has same number of lines as new window
// assume automatically opened new window, and close it
NSUInteger oldTabLines = linesOfHistory(tab);
while([newTab busy]){
[NSThread sleepForTimeInterval:0.1f];
}
NSUInteger newTabLines = linesOfHistory(newTab);
if(oldTabLines == newTabLines){
[win closeSaving:TerminalSaveOptionsNo savingIn:nil];
}
}
}
if ([terminalApp isEqualToString:@"ghostty"]) {
openInGhostty(url);
} else {
openInTerminal(url);
}


[terminal activate];
} else {
NSLog(@"[cdto] url is nil, doing nothing");
}
}
}
Loading