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
4 changes: 2 additions & 2 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[submodule "ghostty"]
path = ghostty
url = https://github.com/manaflow-ai/ghostty.git
branch = main
url = https://github.com/mdsakalu/ghostty.git
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot Mar 9, 2026

Choose a reason for hiding this comment

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

P2: Use the organization-maintained Ghostty fork for the submodule URL instead of a personal fork to avoid a brittle build dependency.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .gitmodules, line 3:

<comment>Use the organization-maintained Ghostty fork for the submodule URL instead of a personal fork to avoid a brittle build dependency.</comment>

<file context>
@@ -1,7 +1,7 @@
 	path = ghostty
-	url = https://github.com/manaflow-ai/ghostty.git
-	branch = main
+	url = https://github.com/mdsakalu/ghostty.git
+	branch = zmx-termio-backend
 [submodule "homebrew-cmux"]
</file context>
Suggested change
url = https://github.com/mdsakalu/ghostty.git
url = https://github.com/manaflow-ai/ghostty.git
Fix with Cubic

branch = zmx-termio-backend
Comment on lines +3 to +4
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify the ghostty submodule commit exists on the remote fork

# Get the submodule commit SHA
cd ghostty 2>/dev/null && COMMIT=$(git rev-parse HEAD) && cd ..

if [ -n "$COMMIT" ]; then
  echo "Submodule commit: $COMMIT"
  # Check if commit exists on remote
  git ls-remote https://github.com/mdsakalu/ghostty.git | grep -q "$COMMIT" && echo "✓ Commit exists on remote" || echo "✗ Commit NOT found on remote"
else
  echo "Could not determine submodule commit"
fi

# Check if fork documentation exists
echo ""
echo "Checking for fork documentation:"
fd -t f "ghostty-fork.md" docs/ 2>/dev/null || echo "docs/ghostty-fork.md not found"

Repository: manaflow-ai/cmux

Length of output: 202


🏁 Script executed:

cat docs/ghostty-fork.md

Repository: manaflow-ai/cmux

Length of output: 1724


This PR violates critical submodule management requirements and cannot be merged as-is.

Two blocking issues:

  1. Submodule commit not on remote: The local commit 9c5e6e58f3fc3c298be1dc8e7fbdaa1beaf0746f must be pushed to https://github.com/mdsakalu/ghostty.git on the zmx-termio-backend branch before updating the parent repo pointer.

  2. Fork documentation out of date: docs/ghostty-fork.md still documents manaflow-ai/ghostty and its changes (OSC 99 parser, display link restart). This file must be rewritten to document the mdsakalu/ghostty fork, its purpose (zmx-termio-backend support), the changes it contains, and any merge conflict notes relevant to that branch.

Per repository guidelines: submodule commits must exist on their remote branch before the parent repo can reference them, and fork documentation must be kept in sync with actual fork changes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.gitmodules around lines 3 - 4, Update the submodule and docs to meet repo
policy: push the local submodule commit 9c5e6e58f3fc3c298be1dc8e7fbdaa1beaf0746f
to the remote https://github.com/mdsakalu/ghostty.git on branch
zmx-termio-backend so the parent repo can safely reference it (ensure git push
origin zmx-termio-backend contains that commit), and rewrite
docs/ghostty-fork.md to replace references to manaflow-ai/ghostty with
mdsakalu/ghostty, clearly describe the fork’s purpose (zmx-termio-backend
support), list the specific changes (e.g., OSC 99 parser/display link restart
behavior if applicable or note they differ), and add any merge-conflict notes
relevant to that branch before re-submitting the PR.

[submodule "homebrew-cmux"]
path = homebrew-cmux
url = https://github.com/manaflow-ai/homebrew-cmux.git
Expand Down
4 changes: 4 additions & 0 deletions GhosttyTabs.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
A5001209 /* WindowToolbarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001219 /* WindowToolbarController.swift */; };
A5001240 /* WindowDecorationsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001241 /* WindowDecorationsController.swift */; };
A5001610 /* SessionPersistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001611 /* SessionPersistence.swift */; };
A5001640 /* ZmxSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001641 /* ZmxSupport.swift */; };
A5001100 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A5001101 /* Assets.xcassets */; };
A5001230 /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = A5001231 /* Sparkle */; };
B9000002A1B2C3D4E5F60719 /* cmux.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9000001A1B2C3D4E5F60719 /* cmux.swift */; };
Expand Down Expand Up @@ -204,6 +205,7 @@
A5001223 /* UpdateLogStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Update/UpdateLogStore.swift; sourceTree = "<group>"; };
A5001241 /* WindowDecorationsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowDecorationsController.swift; sourceTree = "<group>"; };
A5001611 /* SessionPersistence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionPersistence.swift; sourceTree = "<group>"; };
A5001641 /* ZmxSupport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZmxSupport.swift; sourceTree = "<group>"; };
818DBCD4AB69EB72573E8138 /* SidebarResizeUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarResizeUITests.swift; sourceTree = "<group>"; };
B8F266256A1A3D9A45BD840F /* SidebarHelpMenuUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarHelpMenuUITests.swift; sourceTree = "<group>"; };
C0B4D9B1A1B2C3D4E5F60718 /* UpdatePillUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdatePillUITests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -395,6 +397,7 @@
A5001241 /* WindowDecorationsController.swift */,
A5001222 /* WindowAccessor.swift */,
A5001611 /* SessionPersistence.swift */,
A5001641 /* ZmxSupport.swift */,
);
path = Sources;
sourceTree = "<group>";
Expand Down Expand Up @@ -662,6 +665,7 @@
A5001240 /* WindowDecorationsController.swift in Sources */,
A500120C /* WindowAccessor.swift in Sources */,
A5001610 /* SessionPersistence.swift in Sources */,
A5001640 /* ZmxSupport.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
136 changes: 136 additions & 0 deletions Resources/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -53589,6 +53589,142 @@
}
}
},
"settings.zmx.persistence.enable.subtitle.disabled": {
"extractionState": "manual",
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Terminals start fresh on each launch."
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "ターミナルは起動のたびに新しく開始されます。"
}
}
}
},
"settings.zmx.persistence.enable.subtitle.enabled": {
"extractionState": "manual",
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Terminal sessions survive app restart via zmx."
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "ターミナルセッションはzmxによりアプリ再起動後も保持されます。"
}
}
}
},
"settings.zmx.persistence.enable.title": {
"extractionState": "manual",
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Enable zmx Session Persistence"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "zmxセッション永続化を有効にする"
}
}
}
},
"settings.zmx.persistence.killOnClose.subtitle.disabled": {
"extractionState": "manual",
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "zmx sessions are preserved when a workspace is closed."
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "ワークスペースを閉じてもzmxセッションは保持されます。"
}
}
}
},
"settings.zmx.persistence.killOnClose.subtitle.enabled": {
"extractionState": "manual",
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Closing a workspace terminates its zmx sessions."
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "ワークスペースを閉じると、そのzmxセッションも終了します。"
}
}
}
},
"settings.zmx.persistence.killOnClose.title": {
"extractionState": "manual",
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Kill Sessions on Workspace Close"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "ワークスペースを閉じるときにセッションを終了する"
}
}
}
},
"settings.zmx.persistence.note.requirements": {
"extractionState": "manual",
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Requires zmx to be installed and on PATH."
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "zmxがインストールされ、PATHに含まれている必要があります。"
}
}
}
},
"settings.zmx.persistence.title": {
"extractionState": "manual",
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "zmx Session Persistence"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "zmxセッション永続化"
}
}
}
},
"settings.section.automation": {
"extractionState": "manual",
"localizations": {
Expand Down
18 changes: 15 additions & 3 deletions Sources/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1638,6 +1638,10 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent
}

func applicationDidFinishLaunching(_ notification: Notification) {
// Don't leak parent zmx session into child terminals.
// Each cmux terminal gets its own zmx session via the ghostty zmx backend config.
unsetenv("ZMX_SESSION")

let env = ProcessInfo.processInfo.environment
let isRunningUnderXCTest = isRunningUnderXCTest(env)
let telemetryEnabled = TelemetrySettings.enabledForCurrentLaunch
Expand Down Expand Up @@ -8725,6 +8729,11 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent
// is removed when the last window closes.
persistWindowGeometry(from: window)
guard let removed = unregisterMainWindowContext(for: window) else { return }
if !isTerminatingApp {
for workspace in removed.tabManager.tabs {
workspace.killZmxSessions()
}
}
commandPaletteVisibilityByWindowId.removeValue(forKey: removed.windowId)
commandPalettePendingOpenByWindowId.removeValue(forKey: removed.windowId)
commandPaletteRecentRequestAtByWindowId.removeValue(forKey: removed.windowId)
Expand Down Expand Up @@ -8798,11 +8807,14 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent
contextContainingTabId(tabId)?.tabManager
}

func closeMainWindowContainingTabId(_ tabId: UUID) {
guard let context = contextContainingTabId(tabId) else { return }
@discardableResult
func closeMainWindowContainingTabId(_ tabId: UUID) -> Bool {
guard let context = contextContainingTabId(tabId) else { return false }
let expectedIdentifier = "cmux.main.\(context.windowId.uuidString)"
let window: NSWindow? = context.window ?? NSApp.windows.first(where: { $0.identifier?.rawValue == expectedIdentifier })
window?.performClose(nil)
guard let window else { return false }
window.performClose(nil)
return true
}

@discardableResult
Expand Down
Loading