Release v1.23.0 (#31)
* Rename cmuxterm to cmux across entire codebase - Rename GitHub repos: manaflow-ai/cmuxterm -> manaflow-ai/cmux, manaflow-ai/homebrew-cmuxterm -> manaflow-ai/homebrew-cmux - Rename bundle IDs: com.cmuxterm.app -> com.cmux.app - Rename CLI: CLI/cmuxterm.swift -> CLI/cmux.swift - Rename homebrew submodule: homebrew-cmuxterm -> homebrew-cmux - Update all socket paths: /tmp/cmuxterm*.sock -> /tmp/cmux*.sock - Update all GitHub URLs, DMG names, Sparkle URLs - Update all source files, scripts, tests, docs, CI workflows * Bump version to 1.23.0
This commit is contained in:
parent
9ba1d02b2b
commit
9817d131f8
70 changed files with 466 additions and 450 deletions
|
|
@ -1,6 +1,6 @@
|
|||
# Release
|
||||
|
||||
Prepare a new release for cmuxterm. This command updates the changelog, bumps the version, creates a PR, monitors CI, and then merges and tags.
|
||||
Prepare a new release for cmux. This command updates the changelog, bumps the version, creates a PR, monitors CI, and then merges and tags.
|
||||
|
||||
## Steps
|
||||
|
||||
|
|
@ -51,8 +51,8 @@ Prepare a new release for cmuxterm. This command updates the changelog, bumps th
|
|||
- Push tag: `git push origin vX.Y.Z`
|
||||
|
||||
11. **Monitor the release workflow**
|
||||
- Watch: `gh run watch --repo manaflow-ai/cmuxterm`
|
||||
- Verify the release appears at: https://github.com/manaflow-ai/cmuxterm/releases
|
||||
- Watch: `gh run watch --repo manaflow-ai/cmux`
|
||||
- Verify the release appears at: https://github.com/manaflow-ai/cmux/releases
|
||||
- Check that the DMG is attached to the release
|
||||
|
||||
## Changelog Guidelines
|
||||
|
|
|
|||
20
.github/workflows/release.yml
vendored
20
.github/workflows/release.yml
vendored
|
|
@ -85,11 +85,11 @@ jobs:
|
|||
|
||||
- name: Inject Sparkle keys into Info.plist
|
||||
run: |
|
||||
APP_PLIST="build/Build/Products/Release/cmuxterm.app/Contents/Info.plist"
|
||||
APP_PLIST="build/Build/Products/Release/cmux.app/Contents/Info.plist"
|
||||
echo "Adding SUPublicEDKey to Info.plist..."
|
||||
/usr/libexec/PlistBuddy -c "Add :SUPublicEDKey string ${SPARKLE_PUBLIC_KEY}" "$APP_PLIST"
|
||||
echo "Adding SUFeedURL to Info.plist..."
|
||||
/usr/libexec/PlistBuddy -c "Add :SUFeedURL string https://github.com/manaflow-ai/cmuxterm/releases/latest/download/appcast.xml" "$APP_PLIST"
|
||||
/usr/libexec/PlistBuddy -c "Add :SUFeedURL string https://github.com/manaflow-ai/cmux/releases/latest/download/appcast.xml" "$APP_PLIST"
|
||||
echo "Verifying:"
|
||||
/usr/libexec/PlistBuddy -c "Print :SUPublicEDKey" "$APP_PLIST"
|
||||
/usr/libexec/PlistBuddy -c "Print :SUFeedURL" "$APP_PLIST"
|
||||
|
|
@ -125,8 +125,8 @@ jobs:
|
|||
echo "Missing APPLE_SIGNING_IDENTITY secret" >&2
|
||||
exit 1
|
||||
fi
|
||||
APP_PATH="build/Build/Products/Release/cmuxterm.app"
|
||||
CLI_PATH="$APP_PATH/Contents/Resources/bin/cmuxterm"
|
||||
APP_PATH="build/Build/Products/Release/cmux.app"
|
||||
CLI_PATH="$APP_PATH/Contents/Resources/bin/cmux"
|
||||
if [ -f "$CLI_PATH" ]; then
|
||||
/usr/bin/codesign --force --options runtime --timestamp --sign "$APPLE_SIGNING_IDENTITY" "$CLI_PATH"
|
||||
fi
|
||||
|
|
@ -144,9 +144,9 @@ jobs:
|
|||
echo "Missing notarization secrets (APPLE_ID, APPLE_APP_SPECIFIC_PASSWORD, APPLE_TEAM_ID)" >&2
|
||||
exit 1
|
||||
fi
|
||||
APP_PATH="build/Build/Products/Release/cmuxterm.app"
|
||||
ZIP_SUBMIT="cmuxterm-notary.zip"
|
||||
DMG_RELEASE="cmuxterm-macos.dmg"
|
||||
APP_PATH="build/Build/Products/Release/cmux.app"
|
||||
ZIP_SUBMIT="cmux-notary.zip"
|
||||
DMG_RELEASE="cmux-macos.dmg"
|
||||
ditto -c -k --sequesterRsrc --keepParent "$APP_PATH" "$ZIP_SUBMIT"
|
||||
APP_SUBMIT_JSON="$(xcrun notarytool submit "$ZIP_SUBMIT" --apple-id "$APPLE_ID" --team-id "$APPLE_TEAM_ID" --password "$APPLE_APP_SPECIFIC_PASSWORD" --wait --output-format json)"
|
||||
APP_SUBMIT_ID="$(python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])' <<<"$APP_SUBMIT_JSON")"
|
||||
|
|
@ -165,7 +165,7 @@ jobs:
|
|||
--identity="$APPLE_SIGNING_IDENTITY" \
|
||||
"$APP_PATH" \
|
||||
./
|
||||
mv ./cmuxterm*.dmg "$DMG_RELEASE"
|
||||
mv ./cmux*.dmg "$DMG_RELEASE"
|
||||
DMG_SUBMIT_JSON="$(xcrun notarytool submit "$DMG_RELEASE" --apple-id "$APPLE_ID" --team-id "$APPLE_TEAM_ID" --password "$APPLE_APP_SPECIFIC_PASSWORD" --wait --output-format json)"
|
||||
DMG_SUBMIT_ID="$(python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])' <<<"$DMG_SUBMIT_JSON")"
|
||||
DMG_STATUS="$(python3 -c 'import json,sys; print(json.load(sys.stdin)["status"])' <<<"$DMG_SUBMIT_JSON")"
|
||||
|
|
@ -185,13 +185,13 @@ jobs:
|
|||
echo "Missing SPARKLE_PRIVATE_KEY secret" >&2
|
||||
exit 1
|
||||
fi
|
||||
./scripts/sparkle_generate_appcast.sh cmuxterm-macos.dmg "$GITHUB_REF_NAME" appcast.xml
|
||||
./scripts/sparkle_generate_appcast.sh cmux-macos.dmg "$GITHUB_REF_NAME" appcast.xml
|
||||
|
||||
- name: Upload release asset
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: |
|
||||
cmuxterm-macos.dmg
|
||||
cmux-macos.dmg
|
||||
appcast.xml
|
||||
generate_release_notes: true
|
||||
|
||||
|
|
|
|||
36
.github/workflows/update-homebrew.yml
vendored
36
.github/workflows/update-homebrew.yml
vendored
|
|
@ -31,31 +31,31 @@ jobs:
|
|||
id: sha
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
curl -sL "https://github.com/manaflow-ai/cmuxterm/releases/download/v${VERSION}/cmuxterm-macos.dmg" -o cmuxterm.dmg
|
||||
SHA256=$(shasum -a 256 cmuxterm.dmg | cut -d' ' -f1)
|
||||
curl -sL "https://github.com/manaflow-ai/cmux/releases/download/v${VERSION}/cmux-macos.dmg" -o cmux.dmg
|
||||
SHA256=$(shasum -a 256 cmux.dmg | cut -d' ' -f1)
|
||||
echo "sha256=$SHA256" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Checkout homebrew-cmuxterm
|
||||
- name: Checkout homebrew-cmux
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: manaflow-ai/homebrew-cmuxterm
|
||||
repository: manaflow-ai/homebrew-cmux
|
||||
token: ${{ secrets.HOMEBREW_TAP_TOKEN }}
|
||||
path: homebrew-cmuxterm
|
||||
path: homebrew-cmux
|
||||
|
||||
- name: Update cask formula
|
||||
env:
|
||||
VERSION: ${{ steps.version.outputs.version }}
|
||||
SHA256: ${{ steps.sha.outputs.sha256 }}
|
||||
run: |
|
||||
cat > homebrew-cmuxterm/Casks/cmuxterm.rb << CASKEOF
|
||||
cask "cmuxterm" do
|
||||
cat > homebrew-cmux/Casks/cmux.rb << CASKEOF
|
||||
cask "cmux" do
|
||||
version "${VERSION}"
|
||||
sha256 "${SHA256}"
|
||||
|
||||
url "https://github.com/manaflow-ai/cmuxterm/releases/download/v#{version}/cmuxterm-macos.dmg"
|
||||
name "cmuxterm"
|
||||
url "https://github.com/manaflow-ai/cmux/releases/download/v#{version}/cmux-macos.dmg"
|
||||
name "cmux"
|
||||
desc "Lightweight native macOS terminal with vertical tabs for AI coding agents"
|
||||
homepage "https://github.com/manaflow-ai/cmuxterm"
|
||||
homepage "https://github.com/manaflow-ai/cmux"
|
||||
|
||||
livecheck do
|
||||
url :url
|
||||
|
|
@ -64,29 +64,29 @@ jobs:
|
|||
|
||||
depends_on macos: ">= :ventura"
|
||||
|
||||
app "cmuxterm.app"
|
||||
app "cmux.app"
|
||||
|
||||
zap trash: [
|
||||
"~/Library/Application Support/cmuxterm",
|
||||
"~/Library/Caches/cmuxterm",
|
||||
"~/Library/Preferences/ai.manaflow.cmuxterm.plist",
|
||||
"~/Library/Application Support/cmux",
|
||||
"~/Library/Caches/cmux",
|
||||
"~/Library/Preferences/ai.manaflow.cmux.plist",
|
||||
]
|
||||
end
|
||||
CASKEOF
|
||||
# Remove leading whitespace from heredoc
|
||||
sed -i 's/^ //' homebrew-cmuxterm/Casks/cmuxterm.rb
|
||||
sed -i 's/^ //' homebrew-cmux/Casks/cmux.rb
|
||||
|
||||
- name: Commit and push
|
||||
env:
|
||||
VERSION: ${{ steps.version.outputs.version }}
|
||||
run: |
|
||||
cd homebrew-cmuxterm
|
||||
cd homebrew-cmux
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git add Casks/cmuxterm.rb
|
||||
git add Casks/cmux.rb
|
||||
if git diff --staged --quiet; then
|
||||
echo "No changes - cask already up to date"
|
||||
else
|
||||
git commit -m "Update cmuxterm to ${VERSION}"
|
||||
git commit -m "Update cmux to ${VERSION}"
|
||||
git push
|
||||
fi
|
||||
|
|
|
|||
6
.gitmodules
vendored
6
.gitmodules
vendored
|
|
@ -2,6 +2,6 @@
|
|||
path = ghostty
|
||||
url = https://github.com/manaflow-ai/ghostty.git
|
||||
branch = main
|
||||
[submodule "homebrew-cmuxterm"]
|
||||
path = homebrew-cmuxterm
|
||||
url = https://github.com/manaflow-ai/homebrew-cmuxterm.git
|
||||
[submodule "homebrew-cmux"]
|
||||
path = homebrew-cmux
|
||||
url = https://github.com/manaflow-ai/homebrew-cmux.git
|
||||
|
|
|
|||
17
CHANGELOG.md
17
CHANGELOG.md
|
|
@ -1,6 +1,15 @@
|
|||
# Changelog
|
||||
|
||||
All notable changes to cmuxterm are documented here.
|
||||
All notable changes to cmux are documented here.
|
||||
|
||||
## [1.23.0] - 2026-02-09
|
||||
|
||||
### Changed
|
||||
- Rename app from cmuxterm to cmux — new app name, bundle ID, socket paths, Homebrew tap, and CLI binary name
|
||||
- Sidebar now shows tab status as text instead of colored dots, with instant git HEAD change detection
|
||||
|
||||
### Fixed
|
||||
- CLI `set-status` command not properly quoting values or routing `--tab` flag
|
||||
|
||||
## [1.22.0] - 2026-02-09
|
||||
|
||||
|
|
@ -114,7 +123,7 @@ All notable changes to cmuxterm are documented here.
|
|||
### Added
|
||||
- Sentry SDK for crash reporting
|
||||
- Documentation site with Fumadocs
|
||||
- Homebrew installation support (`brew install --cask cmuxterm`)
|
||||
- Homebrew installation support (`brew install --cask cmux`)
|
||||
- Auto-update Homebrew cask on release
|
||||
|
||||
### Fixed
|
||||
|
|
@ -160,7 +169,7 @@ All notable changes to cmuxterm are documented here.
|
|||
## [1.4.0] - 2025-01-28
|
||||
|
||||
### Added
|
||||
- cmuxterm CLI with socket control modes
|
||||
- cmux CLI with socket control modes
|
||||
- NSPopover-based notifications
|
||||
|
||||
### Fixed
|
||||
|
|
@ -184,7 +193,7 @@ All notable changes to cmuxterm are documented here.
|
|||
- Update UI error details and pill visibility
|
||||
|
||||
### Changed
|
||||
- Renamed app to cmuxterm
|
||||
- Renamed app to cmux
|
||||
- Improved CI UI test stability
|
||||
|
||||
## [1.1.0] - 2025-01-28
|
||||
|
|
|
|||
10
CLAUDE.md
10
CLAUDE.md
|
|
@ -1,4 +1,4 @@
|
|||
# cmuxterm agent notes
|
||||
# cmux agent notes
|
||||
|
||||
## Initial setup
|
||||
|
||||
|
|
@ -83,7 +83,7 @@ ssh cmux-vm 'cd /Users/cmux/GhosttyTabs && xcodebuild -project GhosttyTabs.xcode
|
|||
Run basic automated tests on the UTM macOS VM (never on the host machine):
|
||||
|
||||
```bash
|
||||
ssh cmux-vm 'cd /Users/cmux/GhosttyTabs && xcodebuild -project GhosttyTabs.xcodeproj -scheme cmux -configuration Debug -destination "platform=macOS" build && pkill -x "cmuxterm DEV" || true && APP=$(find /Users/cmux/Library/Developer/Xcode/DerivedData -path "*/Build/Products/Debug/cmuxterm DEV.app" -print -quit) && open "$APP" && for i in {1..20}; do [ -S /tmp/cmuxterm.sock ] && break; sleep 0.5; done && python3 tests/test_update_timing.py && python3 tests/test_signals_auto.py && python3 tests/test_ctrl_socket.py && python3 tests/test_notifications.py'
|
||||
ssh cmux-vm 'cd /Users/cmux/GhosttyTabs && xcodebuild -project GhosttyTabs.xcodeproj -scheme cmux -configuration Debug -destination "platform=macOS" build && pkill -x "cmux DEV" || true && APP=$(find /Users/cmux/Library/Developer/Xcode/DerivedData -path "*/Build/Products/Debug/cmux DEV.app" -print -quit) && open "$APP" && for i in {1..20}; do [ -S /tmp/cmux.sock ] && break; sleep 0.5; done && python3 tests/test_update_timing.py && python3 tests/test_signals_auto.py && python3 tests/test_ctrl_socket.py && python3 tests/test_notifications.py'
|
||||
```
|
||||
|
||||
## Ghostty submodule workflow
|
||||
|
|
@ -143,13 +143,13 @@ Manual release steps (if not using the command):
|
|||
```bash
|
||||
git tag vX.Y.Z
|
||||
git push origin vX.Y.Z
|
||||
gh run watch --repo manaflow-ai/cmuxterm
|
||||
gh run watch --repo manaflow-ai/cmux
|
||||
```
|
||||
|
||||
Notes:
|
||||
- Requires GitHub secrets: `APPLE_CERTIFICATE_BASE64`, `APPLE_CERTIFICATE_PASSWORD`,
|
||||
`APPLE_SIGNING_IDENTITY`, `APPLE_ID`, `APPLE_APP_SPECIFIC_PASSWORD`, `APPLE_TEAM_ID`.
|
||||
- The release asset is `cmuxterm-macos.dmg` attached to the tag.
|
||||
- README download button points to `releases/latest/download/cmuxterm-macos.dmg`.
|
||||
- The release asset is `cmux-macos.dmg` attached to the tag.
|
||||
- README download button points to `releases/latest/download/cmux-macos.dmg`.
|
||||
- Versioning: bump the minor version for updates unless explicitly asked otherwise.
|
||||
- Changelog: always update both `CHANGELOG.md` and the docs-site version.
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ struct CMUXCLI {
|
|||
let args: [String]
|
||||
|
||||
func run() throws {
|
||||
var socketPath = ProcessInfo.processInfo.environment["CMUX_SOCKET_PATH"] ?? "/tmp/cmuxterm.sock"
|
||||
var socketPath = ProcessInfo.processInfo.environment["CMUX_SOCKET_PATH"] ?? "/tmp/cmux.sock"
|
||||
var jsonOutput = false
|
||||
|
||||
var index = 1
|
||||
|
|
@ -640,10 +640,10 @@ struct CMUXCLI {
|
|||
|
||||
private func usage() -> String {
|
||||
return """
|
||||
cmuxterm - control cmuxterm via Unix socket
|
||||
cmux - control cmux via Unix socket
|
||||
|
||||
Usage:
|
||||
cmuxterm [--socket PATH] [--json] <command> [options]
|
||||
cmux [--socket PATH] [--json] <command> [options]
|
||||
|
||||
Commands:
|
||||
ping
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
# Contributing to cmuxterm
|
||||
# Contributing to cmux
|
||||
|
||||
## Prerequisites
|
||||
|
||||
|
|
@ -10,8 +10,8 @@
|
|||
|
||||
1. Clone the repository with submodules:
|
||||
```bash
|
||||
git clone --recursive https://github.com/manaflow-ai/cmuxterm.git
|
||||
cd cmuxterm
|
||||
git clone --recursive https://github.com/manaflow-ai/cmux.git
|
||||
cd cmux
|
||||
```
|
||||
|
||||
2. Run the setup script:
|
||||
|
|
@ -20,7 +20,7 @@
|
|||
```
|
||||
|
||||
This will:
|
||||
- Initialize git submodules (ghostty, homebrew-cmuxterm)
|
||||
- Initialize git submodules (ghostty, homebrew-cmux)
|
||||
- Build the GhosttyKit.xcframework from source
|
||||
- Create the necessary symlinks
|
||||
|
||||
|
|
@ -53,7 +53,7 @@ zig build -Demit-xcframework=true -Doptimize=ReleaseFast
|
|||
### Basic tests (run on VM)
|
||||
|
||||
```bash
|
||||
ssh cmux-vm 'cd /Users/cmux/GhosttyTabs && xcodebuild -project GhosttyTabs.xcodeproj -scheme cmux -configuration Debug -destination "platform=macOS" build && pkill -x "cmuxterm DEV" || true && APP=$(find /Users/cmux/Library/Developer/Xcode/DerivedData -path "*/Build/Products/Debug/cmuxterm DEV.app" -print -quit) && open "$APP" && for i in {1..20}; do [ -S /tmp/cmuxterm.sock ] && break; sleep 0.5; done && python3 tests/test_update_timing.py && python3 tests/test_signals_auto.py && python3 tests/test_ctrl_socket.py && python3 tests/test_notifications.py'
|
||||
ssh cmux-vm 'cd /Users/cmux/GhosttyTabs && xcodebuild -project GhosttyTabs.xcodeproj -scheme cmux -configuration Debug -destination "platform=macOS" build && pkill -x "cmux DEV" || true && APP=$(find /Users/cmux/Library/Developer/Xcode/DerivedData -path "*/Build/Products/Debug/cmux DEV.app" -print -quit) && open "$APP" && for i in {1..20}; do [ -S /tmp/cmux.sock ] && break; sleep 0.5; done && python3 tests/test_update_timing.py && python3 tests/test_signals_auto.py && python3 tests/test_ctrl_socket.py && python3 tests/test_notifications.py'
|
||||
```
|
||||
|
||||
### UI tests (run on VM)
|
||||
|
|
|
|||
|
|
@ -42,8 +42,8 @@
|
|||
A5001240 /* WindowDecorationsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001241 /* WindowDecorationsController.swift */; };
|
||||
A5001100 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A5001101 /* Assets.xcassets */; };
|
||||
A5001230 /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = A5001231 /* Sparkle */; };
|
||||
B9000002A1B2C3D4E5F60719 /* cmuxterm.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9000001A1B2C3D4E5F60719 /* cmuxterm.swift */; };
|
||||
B900000BA1B2C3D4E5F60719 /* cmuxterm in Copy CLI */ = {isa = PBXBuildFile; fileRef = B9000004A1B2C3D4E5F60719 /* cmuxterm */; };
|
||||
B9000002A1B2C3D4E5F60719 /* cmux.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9000001A1B2C3D4E5F60719 /* cmux.swift */; };
|
||||
B900000BA1B2C3D4E5F60719 /* cmux in Copy CLI */ = {isa = PBXBuildFile; fileRef = B9000004A1B2C3D4E5F60719 /* cmux */; };
|
||||
84E00D47E4584162AE53BC8D /* xterm-ghostty in Resources */ = {isa = PBXBuildFile; fileRef = B2E7294509CC42FE9191870E /* xterm-ghostty */; };
|
||||
C1D2E3F4A5B6C7D8E9F00001 /* shell-integration in Resources */ = {isa = PBXBuildFile; fileRef = C1D2E3F4A5B6C7D8E9F00002 /* shell-integration */; };
|
||||
B9000012A1B2C3D4E5F60719 /* AutomationSocketUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9000011A1B2C3D4E5F60719 /* AutomationSocketUITests.swift */; };
|
||||
|
|
@ -69,7 +69,7 @@
|
|||
dstPath = "bin";
|
||||
dstSubfolderSpec = 7;
|
||||
files = (
|
||||
B900000BA1B2C3D4E5F60719 /* cmuxterm in Copy CLI */,
|
||||
B900000BA1B2C3D4E5F60719 /* cmux in Copy CLI */,
|
||||
);
|
||||
name = "Copy CLI";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
|
@ -88,13 +88,13 @@
|
|||
isa = PBXContainerItemProxy;
|
||||
containerPortal = A5001070 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = B9000005A1B2C3D4E5F60719 /* cmuxterm-cli */;
|
||||
remoteInfo = "cmuxterm-cli";
|
||||
remoteGlobalIDString = B9000005A1B2C3D4E5F60719 /* cmux-cli */;
|
||||
remoteInfo = "cmux-cli";
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
A5001000 /* cmuxterm.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = cmuxterm.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
A5001000 /* cmux.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = cmux.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
7E7E6EF344A568AC7FEE3715 /* GhosttyTabsUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = GhosttyTabsUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
A5001011 /* cmuxApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = cmuxApp.swift; sourceTree = "<group>"; };
|
||||
A5001012 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -135,8 +135,8 @@
|
|||
A5001101 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
B2E7294509CC42FE9191870E /* xterm-ghostty */ = {isa = PBXFileReference; lastKnownFileType = file; path = "ghostty/terminfo/78/xterm-ghostty"; sourceTree = "<group>"; };
|
||||
C1D2E3F4A5B6C7D8E9F00002 /* shell-integration */ = {isa = PBXFileReference; lastKnownFileType = folder; path = "shell-integration"; sourceTree = "<group>"; };
|
||||
B9000001A1B2C3D4E5F60719 /* cmuxterm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = cmuxterm.swift; sourceTree = "<group>"; };
|
||||
B9000004A1B2C3D4E5F60719 /* cmuxterm */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = cmuxterm; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
B9000001A1B2C3D4E5F60719 /* cmux.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = cmux.swift; sourceTree = "<group>"; };
|
||||
B9000004A1B2C3D4E5F60719 /* cmux */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = cmux; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
B9000011A1B2C3D4E5F60719 /* AutomationSocketUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutomationSocketUITests.swift; sourceTree = "<group>"; };
|
||||
B9000013A1B2C3D4E5F60719 /* JumpToUnreadUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JumpToUnreadUITests.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
|
@ -204,7 +204,7 @@
|
|||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "set -euo pipefail\nDEST=\"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}\"\nGHOSTTY_DEST=\"${DEST}/ghostty\"\nTERMINFO_DEST=\"${DEST}/terminfo\"\nSRC_SHARE=\"${SRCROOT}/ghostty/zig-out/share\"\nGHOSTTY_SRC=\"${SRC_SHARE}/ghostty\"\nTERMINFO_SRC=\"${SRC_SHARE}/terminfo\"\nFALLBACK_GHOSTTY=\"${SRCROOT}/Resources/ghostty\"\nFALLBACK_TERMINFO=\"${SRCROOT}/Resources/ghostty/terminfo\"\nTERMINFO_OVERLAY=\"${SRCROOT}/Resources/terminfo-overlay\"\nif [ -d \"$GHOSTTY_SRC\" ]; then\n mkdir -p \"$GHOSTTY_DEST\"\n rsync -a --delete \"$GHOSTTY_SRC/\" \"$GHOSTTY_DEST/\"\nelif [ -d \"$FALLBACK_GHOSTTY\" ]; then\n mkdir -p \"$GHOSTTY_DEST\"\n rsync -a --delete \"$FALLBACK_GHOSTTY/\" \"$GHOSTTY_DEST/\"\nfi\nif [ -d \"$TERMINFO_SRC\" ]; then\n mkdir -p \"$TERMINFO_DEST\"\n rsync -a --delete \"$TERMINFO_SRC/\" \"$TERMINFO_DEST/\"\nelif [ -d \"$FALLBACK_TERMINFO\" ]; then\n mkdir -p \"$TERMINFO_DEST\"\n rsync -a --delete \"$FALLBACK_TERMINFO/\" \"$TERMINFO_DEST/\"\nfi\n# Overlay any cmuxterm-specific terminfo adjustments.\n# This intentionally does not use --delete so we only patch specific entries.\nif [ -d \"$TERMINFO_OVERLAY\" ]; then\n mkdir -p \"$TERMINFO_DEST\"\n rsync -a \"$TERMINFO_OVERLAY/\" \"$TERMINFO_DEST/\"\nfi\nINFO_PLIST=\"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}\"\nCOMMIT=\"$(git -C \"${SRCROOT}\" rev-parse --short=9 HEAD 2>/dev/null || true)\"\nif [ -n \"$COMMIT\" ] && [ -f \"$INFO_PLIST\" ]; then\n /usr/libexec/PlistBuddy -c \"Set :CMUXCommit $COMMIT\" \"$INFO_PLIST\" >/dev/null 2>&1 || /usr/libexec/PlistBuddy -c \"Add :CMUXCommit string $COMMIT\" \"$INFO_PLIST\" >/dev/null 2>&1 || true\nfi\n";
|
||||
shellScript = "set -euo pipefail\nDEST=\"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}\"\nGHOSTTY_DEST=\"${DEST}/ghostty\"\nTERMINFO_DEST=\"${DEST}/terminfo\"\nSRC_SHARE=\"${SRCROOT}/ghostty/zig-out/share\"\nGHOSTTY_SRC=\"${SRC_SHARE}/ghostty\"\nTERMINFO_SRC=\"${SRC_SHARE}/terminfo\"\nFALLBACK_GHOSTTY=\"${SRCROOT}/Resources/ghostty\"\nFALLBACK_TERMINFO=\"${SRCROOT}/Resources/ghostty/terminfo\"\nTERMINFO_OVERLAY=\"${SRCROOT}/Resources/terminfo-overlay\"\nif [ -d \"$GHOSTTY_SRC\" ]; then\n mkdir -p \"$GHOSTTY_DEST\"\n rsync -a --delete \"$GHOSTTY_SRC/\" \"$GHOSTTY_DEST/\"\nelif [ -d \"$FALLBACK_GHOSTTY\" ]; then\n mkdir -p \"$GHOSTTY_DEST\"\n rsync -a --delete \"$FALLBACK_GHOSTTY/\" \"$GHOSTTY_DEST/\"\nfi\nif [ -d \"$TERMINFO_SRC\" ]; then\n mkdir -p \"$TERMINFO_DEST\"\n rsync -a --delete \"$TERMINFO_SRC/\" \"$TERMINFO_DEST/\"\nelif [ -d \"$FALLBACK_TERMINFO\" ]; then\n mkdir -p \"$TERMINFO_DEST\"\n rsync -a --delete \"$FALLBACK_TERMINFO/\" \"$TERMINFO_DEST/\"\nfi\n# Overlay any cmux-specific terminfo adjustments.\n# This intentionally does not use --delete so we only patch specific entries.\nif [ -d \"$TERMINFO_OVERLAY\" ]; then\n mkdir -p \"$TERMINFO_DEST\"\n rsync -a \"$TERMINFO_OVERLAY/\" \"$TERMINFO_DEST/\"\nfi\nINFO_PLIST=\"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}\"\nCOMMIT=\"$(git -C \"${SRCROOT}\" rev-parse --short=9 HEAD 2>/dev/null || true)\"\nif [ -n \"$COMMIT\" ] && [ -f \"$INFO_PLIST\" ]; then\n /usr/libexec/PlistBuddy -c \"Set :CMUXCommit $COMMIT\" \"$INFO_PLIST\" >/dev/null 2>&1 || /usr/libexec/PlistBuddy -c \"Add :CMUXCommit string $COMMIT\" \"$INFO_PLIST\" >/dev/null 2>&1 || true\nfi\n";
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
|
|
@ -265,7 +265,7 @@
|
|||
B9000003A1B2C3D4E5F60719 /* CLI */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B9000001A1B2C3D4E5F60719 /* cmuxterm.swift */,
|
||||
B9000001A1B2C3D4E5F60719 /* cmux.swift */,
|
||||
);
|
||||
path = CLI;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -282,8 +282,8 @@
|
|||
A5001042 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A5001000 /* cmuxterm.app */,
|
||||
B9000004A1B2C3D4E5F60719 /* cmuxterm */,
|
||||
A5001000 /* cmux.app */,
|
||||
B9000004A1B2C3D4E5F60719 /* cmux */,
|
||||
7E7E6EF344A568AC7FEE3715 /* GhosttyTabsUITests.xctest */,
|
||||
);
|
||||
name = Products;
|
||||
|
|
@ -325,12 +325,12 @@
|
|||
);
|
||||
name = GhosttyTabs;
|
||||
productName = GhosttyTabs;
|
||||
productReference = A5001000 /* cmuxterm.app */;
|
||||
productReference = A5001000 /* cmux.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
B9000005A1B2C3D4E5F60719 /* cmuxterm-cli */ = {
|
||||
B9000005A1B2C3D4E5F60719 /* cmux-cli */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = B9000007A1B2C3D4E5F60719 /* Build configuration list for PBXNativeTarget "cmuxterm-cli" */;
|
||||
buildConfigurationList = B9000007A1B2C3D4E5F60719 /* Build configuration list for PBXNativeTarget "cmux-cli" */;
|
||||
buildPhases = (
|
||||
B9000006A1B2C3D4E5F60719 /* Sources */,
|
||||
B900000CA1B2C3D4E5F60719 /* Frameworks */,
|
||||
|
|
@ -339,9 +339,9 @@
|
|||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = "cmuxterm-cli";
|
||||
productName = cmuxterm;
|
||||
productReference = B9000004A1B2C3D4E5F60719 /* cmuxterm */;
|
||||
name = "cmux-cli";
|
||||
productName = cmux;
|
||||
productReference = B9000004A1B2C3D4E5F60719 /* cmux */;
|
||||
productType = "com.apple.product-type.tool";
|
||||
};
|
||||
CB450DF0F0B3839599082C4D /* GhosttyTabsUITests */ = {
|
||||
|
|
@ -390,7 +390,7 @@
|
|||
projectRoot = "";
|
||||
targets = (
|
||||
A5001050 /* GhosttyTabs */,
|
||||
B9000005A1B2C3D4E5F60719 /* cmuxterm-cli */,
|
||||
B9000005A1B2C3D4E5F60719 /* cmux-cli */,
|
||||
CB450DF0F0B3839599082C4D /* GhosttyTabsUITests */,
|
||||
);
|
||||
};
|
||||
|
|
@ -450,7 +450,7 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
B9000002A1B2C3D4E5F60719 /* cmuxterm.swift in Sources */,
|
||||
B9000002A1B2C3D4E5F60719 /* cmux.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
@ -464,7 +464,7 @@
|
|||
};
|
||||
B900000EA1B2C3D4E5F60719 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = B9000005A1B2C3D4E5F60719 /* cmuxterm-cli */;
|
||||
target = B9000005A1B2C3D4E5F60719 /* cmux-cli */;
|
||||
targetProxy = B900000DA1B2C3D4E5F60719 /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
|
@ -538,23 +538,23 @@
|
|||
CODE_SIGN_ENTITLEMENTS = "";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 33;
|
||||
CURRENT_PROJECT_VERSION = 34;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
ENABLE_HARDENED_RUNTIME = NO;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "cmuxterm DEV";
|
||||
INFOPLIST_KEY_CFBundleName = "cmuxterm DEV";
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "cmux DEV";
|
||||
INFOPLIST_KEY_CFBundleName = "cmux DEV";
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
INFOPLIST_KEY_NSMainStoryboardFile = "";
|
||||
INFOPLIST_KEY_NSPrincipalClass = NSApplication;
|
||||
INFOPLIST_KEY_SUFeedURL = "https://github.com/manaflow-ai/cmuxterm/releases/latest/download/appcast.xml";
|
||||
INFOPLIST_KEY_SUFeedURL = "https://github.com/manaflow-ai/cmux/releases/latest/download/appcast.xml";
|
||||
INFOPLIST_KEY_SUPublicEDKey = "$(SPARKLE_PUBLIC_KEY)";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.22.0;
|
||||
MARKETING_VERSION = 1.23.0;
|
||||
OTHER_LDFLAGS = (
|
||||
"-lc++",
|
||||
"-framework",
|
||||
|
|
@ -568,8 +568,8 @@
|
|||
"-framework",
|
||||
Carbon,
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.cmuxterm.app.debug;
|
||||
PRODUCT_NAME = "cmuxterm DEV";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.cmux.app.debug;
|
||||
PRODUCT_NAME = "cmux DEV";
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "cmux-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
|
|
@ -583,23 +583,23 @@
|
|||
CODE_SIGN_ENTITLEMENTS = "";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 33;
|
||||
CURRENT_PROJECT_VERSION = 34;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
ENABLE_HARDENED_RUNTIME = NO;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = cmuxterm;
|
||||
INFOPLIST_KEY_CFBundleName = cmuxterm;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = cmux;
|
||||
INFOPLIST_KEY_CFBundleName = cmux;
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
INFOPLIST_KEY_NSMainStoryboardFile = "";
|
||||
INFOPLIST_KEY_NSPrincipalClass = NSApplication;
|
||||
INFOPLIST_KEY_SUFeedURL = "https://github.com/manaflow-ai/cmuxterm/releases/latest/download/appcast.xml";
|
||||
INFOPLIST_KEY_SUFeedURL = "https://github.com/manaflow-ai/cmux/releases/latest/download/appcast.xml";
|
||||
INFOPLIST_KEY_SUPublicEDKey = "$(SPARKLE_PUBLIC_KEY)";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.22.0;
|
||||
MARKETING_VERSION = 1.23.0;
|
||||
OTHER_LDFLAGS = (
|
||||
"-lc++",
|
||||
"-framework",
|
||||
|
|
@ -614,8 +614,8 @@
|
|||
Carbon,
|
||||
);
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.cmuxterm.app;
|
||||
PRODUCT_NAME = cmuxterm;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.cmux.app;
|
||||
PRODUCT_NAME = cmux;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "cmux-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
|
|
@ -627,8 +627,8 @@
|
|||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||
PRODUCT_NAME = cmuxterm;
|
||||
PRODUCT_MODULE_NAME = cmuxterm_cli;
|
||||
PRODUCT_NAME = cmux;
|
||||
PRODUCT_MODULE_NAME = cmux_cli;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
|
|
@ -640,8 +640,8 @@
|
|||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||
PRODUCT_NAME = cmuxterm;
|
||||
PRODUCT_MODULE_NAME = cmuxterm_cli;
|
||||
PRODUCT_NAME = cmux;
|
||||
PRODUCT_MODULE_NAME = cmux_cli;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
SWIFT_VERSION = 5.0;
|
||||
|
|
@ -652,12 +652,12 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 33;
|
||||
CURRENT_PROJECT_VERSION = 34;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||
MARKETING_VERSION = 1.22.0;
|
||||
MARKETING_VERSION = 1.23.0;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.cmuxterm.appuitests;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.cmux.appuitests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
|
|
@ -669,12 +669,12 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 33;
|
||||
CURRENT_PROJECT_VERSION = 34;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||
MARKETING_VERSION = 1.22.0;
|
||||
MARKETING_VERSION = 1.23.0;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.cmuxterm.appuitests;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.cmux.appuitests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TEST_TARGET_NAME = GhosttyTabs;
|
||||
|
|
@ -734,7 +734,7 @@
|
|||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
B9000007A1B2C3D4E5F60719 /* Build configuration list for PBXNativeTarget "cmuxterm-cli" */ = {
|
||||
B9000007A1B2C3D4E5F60719 /* Build configuration list for PBXNativeTarget "cmux-cli" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
B9000008A1B2C3D4E5F60719 /* Debug */,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
<BuildAction parallelizeBuildables="YES" buildImplicitDependencies="YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry buildForTesting="YES" buildForRunning="YES" buildForProfiling="YES" buildForArchiving="YES" buildForAnalyzing="YES">
|
||||
<BuildableReference BuildableIdentifier="primary" BlueprintIdentifier="A5001050" BuildableName="cmuxterm.app" BlueprintName="GhosttyTabs" ReferencedContainer="container:GhosttyTabs.xcodeproj"/>
|
||||
<BuildableReference BuildableIdentifier="primary" BlueprintIdentifier="A5001050" BuildableName="cmux.app" BlueprintName="GhosttyTabs" ReferencedContainer="container:GhosttyTabs.xcodeproj"/>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
|
|
@ -14,17 +14,17 @@
|
|||
</TestableReference>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference BuildableIdentifier="primary" BlueprintIdentifier="A5001050" BuildableName="cmuxterm.app" BlueprintName="GhosttyTabs" ReferencedContainer="container:GhosttyTabs.xcodeproj"/>
|
||||
<BuildableReference BuildableIdentifier="primary" BlueprintIdentifier="A5001050" BuildableName="cmux.app" BlueprintName="GhosttyTabs" ReferencedContainer="container:GhosttyTabs.xcodeproj"/>
|
||||
</MacroExpansion>
|
||||
</TestAction>
|
||||
<LaunchAction buildConfiguration="Release" selectedDebuggerIdentifier="Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier="Xcode.DebuggerFoundation.Launcher.LLDB" launchStyle="0" useCustomWorkingDirectory="NO" ignoresPersistentStateOnLaunch="NO" debugDocumentVersioning="YES" allowLocationSimulation="YES">
|
||||
<BuildableProductRunnable runnableDebuggingMode="0">
|
||||
<BuildableReference BuildableIdentifier="primary" BlueprintIdentifier="A5001050" BuildableName="cmuxterm.app" BlueprintName="GhosttyTabs" ReferencedContainer="container:GhosttyTabs.xcodeproj"/>
|
||||
<BuildableReference BuildableIdentifier="primary" BlueprintIdentifier="A5001050" BuildableName="cmux.app" BlueprintName="GhosttyTabs" ReferencedContainer="container:GhosttyTabs.xcodeproj"/>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction buildConfiguration="Release" shouldUseLaunchSchemeArgsEnv="YES" savedToolIdentifier="" useCustomWorkingDirectory="NO" debugDocumentVersioning="YES">
|
||||
<BuildableProductRunnable runnableDebuggingMode="0">
|
||||
<BuildableReference BuildableIdentifier="primary" BlueprintIdentifier="A5001050" BuildableName="cmuxterm.app" BlueprintName="GhosttyTabs" ReferencedContainer="container:GhosttyTabs.xcodeproj"/>
|
||||
<BuildableReference BuildableIdentifier="primary" BlueprintIdentifier="A5001050" BuildableName="cmux.app" BlueprintName="GhosttyTabs" ReferencedContainer="container:GhosttyTabs.xcodeproj"/>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction buildConfiguration="Release"/>
|
||||
|
|
|
|||
|
|
@ -3,14 +3,14 @@ import Foundation
|
|||
|
||||
final class AutomationSocketUITests: XCTestCase {
|
||||
private var socketPath = ""
|
||||
private let defaultsDomain = "com.cmuxterm.app.debug"
|
||||
private let defaultsDomain = "com.cmux.app.debug"
|
||||
private let modeKey = "socketControlMode"
|
||||
private let legacyKey = "socketControlEnabled"
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
continueAfterFailure = false
|
||||
socketPath = "/tmp/cmuxterm-debug-\(UUID().uuidString).sock"
|
||||
socketPath = "/tmp/cmux-debug-\(UUID().uuidString).sock"
|
||||
resetSocketDefaults()
|
||||
removeSocketFile()
|
||||
}
|
||||
|
|
@ -75,7 +75,7 @@ final class AutomationSocketUITests: XCTestCase {
|
|||
guard let entries = try? FileManager.default.contentsOfDirectory(atPath: tmpPath) else {
|
||||
return nil
|
||||
}
|
||||
let matches = entries.filter { $0.hasPrefix("cmuxterm") && $0.hasSuffix(".sock") }
|
||||
let matches = entries.filter { $0.hasPrefix("cmux") && $0.hasSuffix(".sock") }
|
||||
if let debug = matches.first(where: { $0.contains("debug") }) {
|
||||
return (tmpPath as NSString).appendingPathComponent(debug)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ final class UpdatePillUITests: XCTestCase {
|
|||
private func launchAppWithMockFeed(mode: String, version: String, timingPath: URL? = nil) -> XCUIApplication {
|
||||
let app = XCUIApplication()
|
||||
app.launchEnvironment["CMUX_UI_TEST_MODE"] = "1"
|
||||
app.launchEnvironment["CMUX_UI_TEST_FEED_URL"] = "https://cmuxterm.test/appcast.xml"
|
||||
app.launchEnvironment["CMUX_UI_TEST_FEED_URL"] = "https://cmux.test/appcast.xml"
|
||||
app.launchEnvironment["CMUX_UI_TEST_FEED_MODE"] = mode
|
||||
app.launchEnvironment["CMUX_UI_TEST_UPDATE_VERSION"] = version
|
||||
app.launchEnvironment["CMUX_UI_TEST_AUTO_ALLOW_PERMISSION"] = "1"
|
||||
|
|
|
|||
18
README.md
18
README.md
|
|
@ -1,14 +1,14 @@
|
|||
<h1 align="center">cmuxterm</h1>
|
||||
<h1 align="center">cmux</h1>
|
||||
<p align="center">A Ghostty-based macOS terminal with vertical tabs and notifications for AI coding agents</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/manaflow-ai/cmuxterm/releases/latest/download/cmuxterm-macos.dmg">
|
||||
<img src="./docs/assets/macos-badge.png" alt="Download cmuxterm for macOS" width="180" />
|
||||
<a href="https://github.com/manaflow-ai/cmux/releases/latest/download/cmux-macos.dmg">
|
||||
<img src="./docs/assets/macos-badge.png" alt="Download cmux for macOS" width="180" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="./docs/assets/screenshot.png" alt="cmuxterm screenshot" width="900" />
|
||||
<img src="./docs/assets/screenshot.png" alt="cmux screenshot" width="900" />
|
||||
</p>
|
||||
|
||||
## Features
|
||||
|
|
@ -25,14 +25,14 @@
|
|||
**Homebrew:**
|
||||
|
||||
```bash
|
||||
brew tap manaflow-ai/cmuxterm
|
||||
brew install --cask cmuxterm
|
||||
brew tap manaflow-ai/cmux
|
||||
brew install --cask cmux
|
||||
```
|
||||
|
||||
Or [download the DMG](https://github.com/manaflow-ai/cmuxterm/releases/latest/download/cmuxterm-macos.dmg) directly.
|
||||
Or [download the DMG](https://github.com/manaflow-ai/cmux/releases/latest/download/cmux-macos.dmg) directly.
|
||||
|
||||
## Why cmuxterm?
|
||||
## Why cmux?
|
||||
|
||||
Running multiple AI coding agents? cmuxterm helps you manage them. Instead of losing track of which terminal needs input, the notification panel shows you exactly where to look.
|
||||
Running multiple AI coding agents? cmux helps you manage them. Instead of losing track of which terminal needs input, the notification panel shows you exactly where to look.
|
||||
|
||||
A native macOS app means it launches instantly, uses minimal RAM, and feels right at home on your Mac.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# vim:ft=zsh
|
||||
#
|
||||
# Compatibility shim: with the current integration model, cmuxterm restores
|
||||
# Compatibility shim: with the current integration model, cmux restores
|
||||
# ZDOTDIR in .zshenv so this file should never be reached. If it is, restore
|
||||
# ZDOTDIR and behave like vanilla zsh by sourcing the user's .zlogin.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# vim:ft=zsh
|
||||
#
|
||||
# Compatibility shim: with the current integration model, cmuxterm restores
|
||||
# Compatibility shim: with the current integration model, cmux restores
|
||||
# ZDOTDIR in .zshenv so this file should never be reached. If it is, restore
|
||||
# ZDOTDIR and behave like vanilla zsh by sourcing the user's .zprofile.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
# vim:ft=zsh
|
||||
#
|
||||
# cmuxterm ZDOTDIR bootstrap for zsh.
|
||||
# cmux ZDOTDIR bootstrap for zsh.
|
||||
#
|
||||
# GhosttyKit already uses a ZDOTDIR injection mechanism for zsh (setting ZDOTDIR
|
||||
# to Ghostty's integration dir). cmuxterm also needs to run its integration, but
|
||||
# to Ghostty's integration dir). cmux also needs to run its integration, but
|
||||
# we must restore the user's real ZDOTDIR immediately so that:
|
||||
# - /etc/zshrc sets HISTFILE relative to the real ZDOTDIR/HOME (shared history)
|
||||
# - zsh loads the user's real .zprofile/.zshrc normally (no wrapper recursion)
|
||||
#
|
||||
# We restore ZDOTDIR from (in priority order):
|
||||
# - GHOSTTY_ZSH_ZDOTDIR (set by GhosttyKit when it overwrote ZDOTDIR)
|
||||
# - CMUX_ZSH_ZDOTDIR (set by cmuxterm when it overwrote a user-provided ZDOTDIR)
|
||||
# - CMUX_ZSH_ZDOTDIR (set by cmux when it overwrote a user-provided ZDOTDIR)
|
||||
# - unset (zsh treats unset ZDOTDIR as $HOME)
|
||||
|
||||
if [[ -n "${GHOSTTY_ZSH_ZDOTDIR+X}" ]]; then
|
||||
|
|
@ -36,7 +36,7 @@ fi
|
|||
[[ -r "$_cmux_ghostty" ]] && builtin source -- "$_cmux_ghostty"
|
||||
fi
|
||||
|
||||
# Load cmuxterm integration (unless disabled)
|
||||
# Load cmux integration (unless disabled)
|
||||
if [[ "${CMUX_SHELL_INTEGRATION:-1}" != "0" && -n "${CMUX_SHELL_INTEGRATION_DIR:-}" ]]; then
|
||||
builtin typeset _cmux_integ="$CMUX_SHELL_INTEGRATION_DIR/cmux-zsh-integration.zsh"
|
||||
[[ -r "$_cmux_integ" ]] && builtin source -- "$_cmux_integ"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# vim:ft=zsh
|
||||
#
|
||||
# Compatibility shim: with the current integration model, cmuxterm restores
|
||||
# Compatibility shim: with the current integration model, cmux restores
|
||||
# ZDOTDIR in .zshenv so this file should never be reached. If it is, restore
|
||||
# ZDOTDIR and behave like vanilla zsh by sourcing the user's .zshrc.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# cmuxterm shell integration for bash
|
||||
# cmux shell integration for bash
|
||||
|
||||
_cmux_send() {
|
||||
local payload="$1"
|
||||
|
|
@ -10,7 +10,7 @@ _cmux_send() {
|
|||
# Some nc builds don't support unix sockets, but keep as a last-ditch fallback.
|
||||
#
|
||||
# Important: macOS/BSD nc will often wait for the peer to close the socket
|
||||
# after it has finished writing. cmuxterm keeps the connection open, so
|
||||
# after it has finished writing. cmux keeps the connection open, so
|
||||
# a plain `nc -U` can hang indefinitely and leak background processes.
|
||||
#
|
||||
# Prefer flags that guarantee we exit after sending, and fall back to a
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# cmuxterm shell integration for zsh
|
||||
# cmux shell integration for zsh
|
||||
# Injected automatically — do not source manually
|
||||
|
||||
_cmux_send() {
|
||||
|
|
@ -11,7 +11,7 @@ _cmux_send() {
|
|||
# Some nc builds don't support unix sockets, but keep as a last-ditch fallback.
|
||||
#
|
||||
# Important: macOS/BSD nc will often wait for the peer to close the socket
|
||||
# after it has finished writing. cmuxterm keeps the connection open, so
|
||||
# after it has finished writing. cmux keeps the connection open, so
|
||||
# a plain `nc -U` can hang indefinitely and leak background processes.
|
||||
#
|
||||
# Prefer flags that guarantee we exit after sending, and fall back to a
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
# cmuxterm terminfo overlay
|
||||
# cmux terminfo overlay
|
||||
|
||||
cmuxterm ships Ghostty's `xterm-ghostty` terminfo entry, but the embedded
|
||||
renderer in cmuxterm has differed from Ghostty's app renderer in how it treats
|
||||
cmux ships Ghostty's `xterm-ghostty` terminfo entry, but the embedded
|
||||
renderer in cmux has differed from Ghostty's app renderer in how it treats
|
||||
the "bright" SGR 90-97/100-107 sequences.
|
||||
|
||||
This overlay patches the terminfo capabilities so that `tput setaf 8` (and
|
||||
similar "bright" colors) uses 256-color indexed sequences (`38;5;<n>m` /
|
||||
`48;5;<n>m`) rather than SGR 90-97/100-107. This avoids relying on bright SGR
|
||||
handling and fixes zsh-autosuggestions (default `fg=8`) visibility issues in
|
||||
cmuxterm.
|
||||
cmux.
|
||||
|
||||
The build phase `Copy Ghostty Resources` overlays this directory onto the app
|
||||
bundle's `Contents/Resources/terminfo` after copying Ghostty's resources.
|
||||
|
|
|
|||
|
|
@ -47,9 +47,9 @@ struct SocketControlSettings {
|
|||
return override
|
||||
}
|
||||
#if DEBUG
|
||||
return "/tmp/cmuxterm-debug.sock"
|
||||
return "/tmp/cmux-debug.sock"
|
||||
#else
|
||||
return "/tmp/cmuxterm.sock"
|
||||
return "/tmp/cmux.sock"
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -759,13 +759,13 @@ class TabManager: ObservableObject {
|
|||
}
|
||||
|
||||
private func windowTitle(for tab: Tab?) -> String {
|
||||
guard let tab else { return "cmuxterm" }
|
||||
guard let tab else { return "cmux" }
|
||||
let trimmedTitle = tab.title.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
if !trimmedTitle.isEmpty {
|
||||
return trimmedTitle
|
||||
}
|
||||
let trimmedDirectory = tab.currentDirectory.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
return trimmedDirectory.isEmpty ? "cmuxterm" : trimmedDirectory
|
||||
return trimmedDirectory.isEmpty ? "cmux" : trimmedDirectory
|
||||
}
|
||||
|
||||
func focusTab(_ tabId: UUID, surfaceId: UUID? = nil, suppressFlash: Bool = false) {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import Foundation
|
|||
class TerminalController {
|
||||
static let shared = TerminalController()
|
||||
|
||||
private var socketPath = "/tmp/cmuxterm.sock"
|
||||
private var socketPath = "/tmp/cmux.sock"
|
||||
private var serverSocket: Int32 = -1
|
||||
private var isRunning = false
|
||||
private var clientHandlers: [Int32: Thread] = [:]
|
||||
|
|
|
|||
|
|
@ -34,8 +34,8 @@ struct TerminalNotification: Identifiable, Hashable {
|
|||
final class TerminalNotificationStore: ObservableObject {
|
||||
static let shared = TerminalNotificationStore()
|
||||
|
||||
static let categoryIdentifier = "com.cmuxterm.app.userNotification"
|
||||
static let actionShowIdentifier = "com.cmuxterm.app.userNotification.show"
|
||||
static let categoryIdentifier = "com.cmux.app.userNotification"
|
||||
static let actionShowIdentifier = "com.cmux.app.userNotification.show"
|
||||
|
||||
@Published private(set) var notifications: [TerminalNotification] = []
|
||||
|
||||
|
|
@ -176,7 +176,7 @@ final class TerminalNotificationStore: ObservableObject {
|
|||
let content = UNMutableNotificationContent()
|
||||
let appName = Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") as? String
|
||||
?? Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String
|
||||
?? "cmuxterm"
|
||||
?? "cmux"
|
||||
content.title = notification.title.isEmpty ? appName : notification.title
|
||||
content.subtitle = notification.subtitle
|
||||
content.body = notification.body
|
||||
|
|
@ -242,8 +242,8 @@ final class TerminalNotificationStore: ObservableObject {
|
|||
self.hasPromptedForSettings = true
|
||||
|
||||
let alert = NSAlert()
|
||||
alert.messageText = "Enable Notifications for cmuxterm"
|
||||
alert.informativeText = "Notifications are disabled for cmuxterm. Enable them in System Settings to see alerts."
|
||||
alert.messageText = "Enable Notifications for cmux"
|
||||
alert.informativeText = "Notifications are disabled for cmux. Enable them in System Settings to see alerts."
|
||||
alert.addButton(withTitle: "Open Settings")
|
||||
alert.addButton(withTitle: "Not Now")
|
||||
let response = alert.runModal()
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import Cocoa
|
|||
import Combine
|
||||
import SwiftUI
|
||||
|
||||
/// Controller for managing Sparkle updates in cmuxterm.
|
||||
/// Controller for managing Sparkle updates in cmux.
|
||||
class UpdateController {
|
||||
private(set) var updater: SPUUpdater
|
||||
private let userDriver: UpdateDriver
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ extension UpdateDriver: SPUUpdaterDelegate {
|
|||
}
|
||||
#endif
|
||||
let infoURL = Bundle.main.object(forInfoDictionaryKey: "SUFeedURL") as? String
|
||||
let fallback = "https://github.com/manaflow-ai/cmuxterm/releases/latest/download/appcast.xml"
|
||||
let fallback = "https://github.com/manaflow-ai/cmux/releases/latest/download/appcast.xml"
|
||||
let feedURLString = (infoURL?.isEmpty == false) ? infoURL! : fallback
|
||||
recordFeedURLString(feedURLString, usedFallback: feedURLString == fallback)
|
||||
return feedURLString
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ class UpdateDriver: NSObject, SPUUserDriver {
|
|||
}
|
||||
|
||||
func showUpdateReleaseNotes(with downloadData: SPUDownloadData) {
|
||||
// cmuxterm uses Sparkle's UI for release notes links instead.
|
||||
// cmux uses Sparkle's UI for release notes links instead.
|
||||
}
|
||||
|
||||
func showUpdateReleaseNotesFailedToDownloadWithError(_ error: any Error) {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import AppKit
|
|||
final class UpdateLogStore {
|
||||
static let shared = UpdateLogStore()
|
||||
|
||||
private let queue = DispatchQueue(label: "cmuxterm.update.log")
|
||||
private let queue = DispatchQueue(label: "cmux.update.log")
|
||||
private var entries: [String] = []
|
||||
private let maxEntries = 200
|
||||
private let maxFileSize: UInt64 = 256 * 1024 // 256 KB
|
||||
|
|
@ -16,7 +16,7 @@ final class UpdateLogStore {
|
|||
formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
|
||||
let logsDir = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).first
|
||||
?? FileManager.default.temporaryDirectory
|
||||
logURL = logsDir.appendingPathComponent("Logs/cmuxterm-update.log")
|
||||
logURL = logsDir.appendingPathComponent("Logs/cmux-update.log")
|
||||
ensureLogFile()
|
||||
}
|
||||
|
||||
|
|
@ -84,7 +84,7 @@ final class UpdateLogStore {
|
|||
final class FocusLogStore {
|
||||
static let shared = FocusLogStore()
|
||||
|
||||
private let queue = DispatchQueue(label: "cmuxterm.focus.log")
|
||||
private let queue = DispatchQueue(label: "cmux.focus.log")
|
||||
private var entries: [String] = []
|
||||
private let maxEntries = 400
|
||||
private let maxFileSize: UInt64 = 256 * 1024 // 256 KB
|
||||
|
|
@ -96,7 +96,7 @@ final class FocusLogStore {
|
|||
formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
|
||||
let logsDir = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).first
|
||||
?? FileManager.default.temporaryDirectory
|
||||
logURL = logsDir.appendingPathComponent("Logs/cmuxterm-focus.log")
|
||||
logURL = logsDir.appendingPathComponent("Logs/cmux-focus.log")
|
||||
ensureLogFile()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ fileprivate struct PermissionRequestView: View {
|
|||
Text("Enable automatic updates?")
|
||||
.font(.system(size: 13, weight: .semibold))
|
||||
|
||||
Text("cmuxterm can automatically check for updates in the background.")
|
||||
Text("cmux can automatically check for updates in the background.")
|
||||
.font(.system(size: 11))
|
||||
.foregroundColor(.secondary)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
|
|
|
|||
|
|
@ -61,13 +61,13 @@ enum UpdateTestSupport {
|
|||
|
||||
private static func makeAppcastItem(displayVersion: String) -> SUAppcastItem? {
|
||||
let enclosure: [String: Any] = [
|
||||
"url": "https://example.com/cmuxterm.zip",
|
||||
"url": "https://example.com/cmux.zip",
|
||||
"length": "1024",
|
||||
"sparkle:version": displayVersion,
|
||||
"sparkle:shortVersionString": displayVersion,
|
||||
]
|
||||
let dict: [String: Any] = [
|
||||
"title": "cmuxterm \(displayVersion)",
|
||||
"title": "cmux \(displayVersion)",
|
||||
"enclosure": enclosure,
|
||||
]
|
||||
return SUAppcastItem(dictionary: dict)
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
import Foundation
|
||||
|
||||
final class UpdateTestURLProtocol: URLProtocol {
|
||||
static let host = "cmuxterm.test"
|
||||
static let host = "cmux.test"
|
||||
static let appcastPath = "/appcast.xml"
|
||||
static let updatePath = "/cmuxterm-test.zip"
|
||||
static let updatePath = "/cmux-test.zip"
|
||||
|
||||
private static var isRegistered = false
|
||||
private static let registrationLock = NSLock()
|
||||
|
|
@ -84,7 +84,7 @@ final class UpdateTestURLProtocol: URLProtocol {
|
|||
} else {
|
||||
item = """
|
||||
<item>
|
||||
<title>cmuxterm \(version)</title>
|
||||
<title>cmux \(version)</title>
|
||||
<sparkle:version>\(version)</sparkle:version>
|
||||
<sparkle:shortVersionString>\(version)</sparkle:shortVersionString>
|
||||
<enclosure url="\(updateURL)" length="\(updateLength)" type="application/octet-stream" />
|
||||
|
|
@ -98,7 +98,7 @@ final class UpdateTestURLProtocol: URLProtocol {
|
|||
xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<channel>
|
||||
<title>cmuxterm Test Updates</title>
|
||||
<title>cmux Test Updates</title>
|
||||
<link>https://\(host)</link>
|
||||
<description>Test updates feed</description>
|
||||
<language>en</language>
|
||||
|
|
@ -111,7 +111,7 @@ final class UpdateTestURLProtocol: URLProtocol {
|
|||
}
|
||||
|
||||
private static func updateArchiveData() -> Data {
|
||||
Data("cmuxterm test update".utf8)
|
||||
Data("cmux test update".utf8)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -216,13 +216,13 @@ class UpdateViewModel: ObservableObject {
|
|||
if let networkError = networkError(from: nsError) {
|
||||
switch networkError.code {
|
||||
case NSURLErrorNotConnectedToInternet:
|
||||
return "cmuxterm can’t reach the update server. Check your internet connection and try again."
|
||||
return "cmux can't reach the update server. Check your internet connection and try again."
|
||||
case NSURLErrorTimedOut:
|
||||
return "The update server took too long to respond. Try again in a moment."
|
||||
case NSURLErrorCannotFindHost:
|
||||
return "The update server can’t be found. Check your connection or try again later."
|
||||
case NSURLErrorCannotConnectToHost:
|
||||
return "cmuxterm couldn’t connect to the update server. Check your connection or try again later."
|
||||
return "cmux couldn't connect to the update server. Check your connection or try again later."
|
||||
case NSURLErrorNetworkConnectionLost:
|
||||
return "The network connection was lost while checking for updates. Try again."
|
||||
case NSURLErrorSecureConnectionFailed,
|
||||
|
|
@ -238,7 +238,7 @@ class UpdateViewModel: ObservableObject {
|
|||
if nsError.domain == SUSparkleErrorDomain {
|
||||
switch nsError.code {
|
||||
case 2001:
|
||||
return "cmuxterm couldn't download the update feed. Check your connection and try again."
|
||||
return "cmux couldn't download the update feed. Check your connection and try again."
|
||||
case 1000, 1002:
|
||||
return "The update feed could not be read. Please try again later."
|
||||
case 4:
|
||||
|
|
@ -248,7 +248,7 @@ class UpdateViewModel: ObservableObject {
|
|||
case 1, 2, 3001, 3002:
|
||||
return "The update's signature could not be verified. Please try again later."
|
||||
case 1003, 1005, 4005:
|
||||
return "Move cmuxterm into Applications and relaunch to enable updates."
|
||||
return "Move cmux into Applications and relaunch to enable updates."
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
|
@ -437,7 +437,7 @@ enum UpdateState: Equatable {
|
|||
|
||||
if let semver = Self.extractSemanticVersion(from: version) {
|
||||
let tag = semver.hasPrefix("v") ? semver : "v\(semver)"
|
||||
if let url = URL(string: "https://github.com/manaflow-ai/cmuxterm/releases/tag/\(tag)") {
|
||||
if let url = URL(string: "https://github.com/manaflow-ai/cmux/releases/tag/\(tag)") {
|
||||
self = .tagged(url)
|
||||
return
|
||||
}
|
||||
|
|
@ -447,7 +447,7 @@ enum UpdateState: Equatable {
|
|||
return nil
|
||||
}
|
||||
|
||||
if let url = URL(string: "https://github.com/manaflow-ai/cmuxterm/commit/\(newHash)") {
|
||||
if let url = URL(string: "https://github.com/manaflow-ai/cmux/commit/\(newHash)") {
|
||||
self = .commit(url)
|
||||
} else {
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ struct cmuxApp: App {
|
|||
.windowResizability(.contentMinSize)
|
||||
.commands {
|
||||
CommandGroup(replacing: .appInfo) {
|
||||
Button("About cmuxterm") {
|
||||
Button("About cmux") {
|
||||
showAboutPanel()
|
||||
}
|
||||
Button("Ghostty Settings…") {
|
||||
|
|
@ -462,7 +462,7 @@ private final class SidebarDebugWindowController: NSWindowController, NSWindowDe
|
|||
private struct AboutPanelView: View {
|
||||
@Environment(\.openURL) private var openURL
|
||||
|
||||
private let githubURL = URL(string: "https://github.com/manaflow-ai/cmuxterm")
|
||||
private let githubURL = URL(string: "https://github.com/manaflow-ai/cmux")
|
||||
private let docsURL = URL(string: "https://term.cmux.dev")
|
||||
|
||||
private var version: String? { Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String }
|
||||
|
|
@ -486,7 +486,7 @@ private struct AboutPanelView: View {
|
|||
|
||||
VStack(alignment: .center, spacing: 32) {
|
||||
VStack(alignment: .center, spacing: 8) {
|
||||
Text("cmuxterm")
|
||||
Text("cmux")
|
||||
.bold()
|
||||
.font(.title)
|
||||
Text("A Ghostty-based terminal with vertical tabs\nand a notification panel for macOS.")
|
||||
|
|
@ -507,7 +507,7 @@ private struct AboutPanelView: View {
|
|||
}
|
||||
let commitText = commit ?? "—"
|
||||
let commitURL = commit.flatMap { hash in
|
||||
URL(string: "https://github.com/manaflow-ai/cmuxterm/commit/\(hash)")
|
||||
URL(string: "https://github.com/manaflow-ai/cmux/commit/\(hash)")
|
||||
}
|
||||
AboutPropertyRow(label: "Commit", text: commitText, url: commitURL)
|
||||
}
|
||||
|
|
|
|||
2
TODO.md
2
TODO.md
|
|
@ -8,7 +8,7 @@
|
|||
- Opens a new terminal
|
||||
- Shows user the diff to their config file (claude.json, opencode config, codex config, etc.)
|
||||
- Prompts user to type 'y' to confirm
|
||||
- Implement as part of `cmuxterm` CLI, menubar just triggers the CLI command
|
||||
- Implement as part of `cmux` CLI, menubar just triggers the CLI command
|
||||
|
||||
## Additional Integrations
|
||||
- [ ] Codex integration
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# cmuxterm Documentation
|
||||
# cmux Documentation
|
||||
|
||||
Documentation website for [cmuxterm](https://github.com/manaflow-ai/cmuxterm), built with [Fumadocs](https://fumadocs.vercel.app) and Next.js.
|
||||
Documentation website for [cmux](https://github.com/manaflow-ai/cmux), built with [Fumadocs](https://fumadocs.vercel.app) and Next.js.
|
||||
|
||||
## Development
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ export default function Layout({ children }: { children: ReactNode }) {
|
|||
<DocsLayout
|
||||
tree={source.pageTree}
|
||||
nav={{
|
||||
title: 'cmuxterm',
|
||||
title: 'cmux',
|
||||
url: '/',
|
||||
}}
|
||||
sidebar={{
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ const inter = Inter({
|
|||
});
|
||||
|
||||
export const metadata = {
|
||||
title: 'cmuxterm Documentation',
|
||||
description: 'Documentation for cmuxterm - A Ghostty-based terminal for AI agents',
|
||||
title: 'cmux Documentation',
|
||||
description: 'Documentation for cmux - A Ghostty-based terminal for AI agents',
|
||||
};
|
||||
|
||||
export default function Layout({ children }: { children: ReactNode }) {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,18 @@
|
|||
---
|
||||
title: Changelog
|
||||
description: Release notes and version history for cmuxterm
|
||||
description: Release notes and version history for cmux
|
||||
---
|
||||
|
||||
All notable changes to cmuxterm are documented here.
|
||||
All notable changes to cmux are documented here.
|
||||
|
||||
## [1.23.0] - 2026-02-09
|
||||
|
||||
### Changed
|
||||
- Rename app from cmuxterm to cmux — new app name, bundle ID, socket paths, Homebrew tap, and CLI binary name
|
||||
- Sidebar now shows tab status as text instead of colored dots, with instant git HEAD change detection
|
||||
|
||||
### Fixed
|
||||
- CLI `set-status` command not properly quoting values or routing `--tab` flag
|
||||
|
||||
## [1.22.0] - 2026-02-09
|
||||
|
||||
|
|
@ -117,7 +126,7 @@ All notable changes to cmuxterm are documented here.
|
|||
### Added
|
||||
- Sentry SDK for crash reporting
|
||||
- Documentation site with Fumadocs
|
||||
- Homebrew installation support (`brew install --cask cmuxterm`)
|
||||
- Homebrew installation support (`brew install --cask cmux`)
|
||||
- Auto-update Homebrew cask on release
|
||||
|
||||
### Fixed
|
||||
|
|
@ -163,7 +172,7 @@ All notable changes to cmuxterm are documented here.
|
|||
## [1.4.0] - 2025-01-28
|
||||
|
||||
### Added
|
||||
- cmuxterm CLI with socket control modes
|
||||
- cmux CLI with socket control modes
|
||||
- NSPopover-based notifications
|
||||
|
||||
### Fixed
|
||||
|
|
@ -187,7 +196,7 @@ All notable changes to cmuxterm are documented here.
|
|||
- Update UI error details and pill visibility
|
||||
|
||||
### Changed
|
||||
- Renamed app to cmuxterm
|
||||
- Renamed app to cmux
|
||||
- Improved CI UI test stability
|
||||
|
||||
## [1.1.0] - 2025-01-28
|
||||
|
|
|
|||
|
|
@ -1,38 +1,38 @@
|
|||
---
|
||||
title: Claude Code Hooks
|
||||
description: Set up notifications for Claude Code using cmuxterm hooks
|
||||
description: Set up notifications for Claude Code using cmux hooks
|
||||
---
|
||||
|
||||
# Claude Code Hooks
|
||||
|
||||
cmuxterm integrates with [Claude Code](https://docs.anthropic.com/en/docs/claude-code) via hooks to notify you when tasks complete or when Claude needs attention.
|
||||
cmux integrates with [Claude Code](https://docs.anthropic.com/en/docs/claude-code) via hooks to notify you when tasks complete or when Claude needs attention.
|
||||
|
||||
## Detecting cmuxterm
|
||||
## Detecting cmux
|
||||
|
||||
Before sending notifications, you should detect if you're running inside cmuxterm to avoid conflicts with other terminals.
|
||||
Before sending notifications, you should detect if you're running inside cmux to avoid conflicts with other terminals.
|
||||
|
||||
### Detection Methods
|
||||
|
||||
**1. Check for the socket:**
|
||||
|
||||
```bash
|
||||
[ -S /tmp/cmuxterm.sock ] && echo "In cmuxterm"
|
||||
[ -S /tmp/cmux.sock ] && echo "In cmux"
|
||||
```
|
||||
|
||||
**2. Check for the CLI:**
|
||||
|
||||
```bash
|
||||
command -v cmuxterm &>/dev/null && echo "cmuxterm available"
|
||||
command -v cmux &>/dev/null && echo "cmux available"
|
||||
```
|
||||
|
||||
**3. Check the TERM_PROGRAM environment variable:**
|
||||
|
||||
```bash
|
||||
[ "$TERM_PROGRAM" = "ghostty" ] && [ -S /tmp/cmuxterm.sock ] && echo "In cmuxterm"
|
||||
[ "$TERM_PROGRAM" = "ghostty" ] && [ -S /tmp/cmux.sock ] && echo "In cmux"
|
||||
```
|
||||
|
||||
<Callout type="info">
|
||||
cmuxterm sets `TERM_PROGRAM=ghostty` since it's built on Ghostty. Use the socket check to distinguish from regular Ghostty.
|
||||
cmux sets `TERM_PROGRAM=ghostty` since it's built on Ghostty. Use the socket check to distinguish from regular Ghostty.
|
||||
</Callout>
|
||||
|
||||
## Setting Up Hooks
|
||||
|
|
@ -46,12 +46,12 @@ Claude Code supports hooks that run on specific events. Add these to your `~/.cl
|
|||
{
|
||||
"matcher": "Task",
|
||||
"hooks": [
|
||||
"/path/to/cmuxterm-notify.sh"
|
||||
"/path/to/cmux-notify.sh"
|
||||
]
|
||||
}
|
||||
],
|
||||
"Stop": [
|
||||
"/path/to/cmuxterm-notify.sh"
|
||||
"/path/to/cmux-notify.sh"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -59,14 +59,14 @@ Claude Code supports hooks that run on specific events. Add these to your `~/.cl
|
|||
|
||||
## Notification Hook Script
|
||||
|
||||
Create a script that checks for cmuxterm and sends notifications:
|
||||
Create a script that checks for cmux and sends notifications:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# ~/.claude/hooks/cmuxterm-notify.sh
|
||||
# ~/.claude/hooks/cmux-notify.sh
|
||||
|
||||
# Only proceed if we're in cmuxterm
|
||||
if [ ! -S /tmp/cmuxterm.sock ]; then
|
||||
# Only proceed if we're in cmux
|
||||
if [ ! -S /tmp/cmux.sock ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
|
|
@ -80,14 +80,14 @@ SESSION=$(echo "$EVENT" | jq -r '.session_id // ""' | cut -c1-8)
|
|||
|
||||
case "$EVENT_TYPE" in
|
||||
"Stop")
|
||||
cmuxterm notify \
|
||||
cmux notify \
|
||||
--title "Claude Code" \
|
||||
--subtitle "Task Complete" \
|
||||
--body "Session $SESSION finished"
|
||||
;;
|
||||
"PostToolUse")
|
||||
if [ "$TOOL_NAME" = "Task" ]; then
|
||||
cmuxterm notify \
|
||||
cmux notify \
|
||||
--title "Claude Code" \
|
||||
--subtitle "Agent Finished" \
|
||||
--body "Task agent completed in session $SESSION"
|
||||
|
|
@ -99,7 +99,7 @@ esac
|
|||
Make it executable:
|
||||
|
||||
```bash
|
||||
chmod +x ~/.claude/hooks/cmuxterm-notify.sh
|
||||
chmod +x ~/.claude/hooks/cmux-notify.sh
|
||||
```
|
||||
|
||||
## Example Configurations
|
||||
|
|
@ -112,7 +112,7 @@ Get notified whenever Claude Code finishes a task:
|
|||
{
|
||||
"hooks": {
|
||||
"Stop": [
|
||||
"~/.claude/hooks/cmuxterm-notify.sh"
|
||||
"~/.claude/hooks/cmux-notify.sh"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -128,7 +128,7 @@ Only notify for Task tool completions (agent subprocesses):
|
|||
"PostToolUse": [
|
||||
{
|
||||
"matcher": "Task",
|
||||
"hooks": ["~/.claude/hooks/cmuxterm-notify.sh"]
|
||||
"hooks": ["~/.claude/hooks/cmux-notify.sh"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -141,9 +141,9 @@ Add error notifications:
|
|||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# cmuxterm-notify.sh with error handling
|
||||
# cmux-notify.sh with error handling
|
||||
|
||||
if [ ! -S /tmp/cmuxterm.sock ]; then
|
||||
if [ ! -S /tmp/cmux.sock ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
|
|
@ -152,11 +152,11 @@ EVENT_TYPE=$(echo "$EVENT" | jq -r '.event // "unknown"')
|
|||
ERROR=$(echo "$EVENT" | jq -r '.error // ""')
|
||||
|
||||
if [ -n "$ERROR" ] && [ "$ERROR" != "null" ]; then
|
||||
cmuxterm notify \
|
||||
cmux notify \
|
||||
--title "Claude Code Error" \
|
||||
--body "$ERROR"
|
||||
elif [ "$EVENT_TYPE" = "Stop" ]; then
|
||||
cmuxterm notify \
|
||||
cmux notify \
|
||||
--title "Claude Code" \
|
||||
--body "Task complete"
|
||||
fi
|
||||
|
|
@ -168,20 +168,20 @@ Only notify if the terminal is not focused:
|
|||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# cmuxterm-notify.sh with focus detection
|
||||
# cmux-notify.sh with focus detection
|
||||
|
||||
if [ ! -S /tmp/cmuxterm.sock ]; then
|
||||
if [ ! -S /tmp/cmux.sock ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# cmuxterm automatically suppresses notifications for focused tabs,
|
||||
# cmux automatically suppresses notifications for focused tabs,
|
||||
# so we can always send - it will handle suppression for us
|
||||
|
||||
EVENT=$(cat)
|
||||
EVENT_TYPE=$(echo "$EVENT" | jq -r '.event // "unknown"')
|
||||
|
||||
if [ "$EVENT_TYPE" = "Stop" ]; then
|
||||
cmuxterm notify \
|
||||
cmux notify \
|
||||
--title "Claude Code" \
|
||||
--subtitle "Ready" \
|
||||
--body "Waiting for your input"
|
||||
|
|
@ -196,7 +196,7 @@ If you prefer not to use the CLI, you can emit OSC sequences directly:
|
|||
#!/bin/bash
|
||||
# Direct OSC notification (no CLI needed)
|
||||
|
||||
if [ ! -S /tmp/cmuxterm.sock ]; then
|
||||
if [ ! -S /tmp/cmux.sock ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
|
|
@ -220,12 +220,12 @@ mkdir -p ~/.claude/hooks
|
|||
2. Create the notification script:
|
||||
|
||||
```bash
|
||||
cat > ~/.claude/hooks/cmuxterm-notify.sh << 'EOF'
|
||||
cat > ~/.claude/hooks/cmux-notify.sh << 'EOF'
|
||||
#!/bin/bash
|
||||
# cmuxterm notification hook for Claude Code
|
||||
# cmux notification hook for Claude Code
|
||||
|
||||
# Skip if not in cmuxterm
|
||||
[ -S /tmp/cmuxterm.sock ] || exit 0
|
||||
# Skip if not in cmux
|
||||
[ -S /tmp/cmux.sock ] || exit 0
|
||||
|
||||
EVENT=$(cat)
|
||||
EVENT_TYPE=$(echo "$EVENT" | jq -r '.event // "unknown"')
|
||||
|
|
@ -233,14 +233,14 @@ TOOL=$(echo "$EVENT" | jq -r '.tool_name // ""')
|
|||
|
||||
case "$EVENT_TYPE" in
|
||||
"Stop")
|
||||
cmuxterm notify --title "Claude Code" --body "Session complete"
|
||||
cmux notify --title "Claude Code" --body "Session complete"
|
||||
;;
|
||||
"PostToolUse")
|
||||
[ "$TOOL" = "Task" ] && cmuxterm notify --title "Claude Code" --body "Agent finished"
|
||||
[ "$TOOL" = "Task" ] && cmux notify --title "Claude Code" --body "Agent finished"
|
||||
;;
|
||||
esac
|
||||
EOF
|
||||
chmod +x ~/.claude/hooks/cmuxterm-notify.sh
|
||||
chmod +x ~/.claude/hooks/cmux-notify.sh
|
||||
```
|
||||
|
||||
3. Configure Claude Code:
|
||||
|
|
@ -249,11 +249,11 @@ chmod +x ~/.claude/hooks/cmuxterm-notify.sh
|
|||
cat > ~/.claude/settings.json << 'EOF'
|
||||
{
|
||||
"hooks": {
|
||||
"Stop": ["~/.claude/hooks/cmuxterm-notify.sh"],
|
||||
"Stop": ["~/.claude/hooks/cmux-notify.sh"],
|
||||
"PostToolUse": [
|
||||
{
|
||||
"matcher": "Task",
|
||||
"hooks": ["~/.claude/hooks/cmuxterm-notify.sh"]
|
||||
"hooks": ["~/.claude/hooks/cmux-notify.sh"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -263,4 +263,4 @@ EOF
|
|||
|
||||
4. Restart Claude Code to apply the hooks.
|
||||
|
||||
Now you'll receive desktop notifications in cmuxterm whenever Claude Code finishes a task or needs attention.
|
||||
Now you'll receive desktop notifications in cmux whenever Claude Code finishes a task or needs attention.
|
||||
|
|
|
|||
|
|
@ -1,19 +1,19 @@
|
|||
---
|
||||
title: CLI Reference
|
||||
description: Command-line interface for cmuxterm
|
||||
description: Command-line interface for cmux
|
||||
---
|
||||
|
||||
# CLI Reference
|
||||
|
||||
cmuxterm includes a command-line tool for controlling the terminal from scripts and other tools.
|
||||
cmux includes a command-line tool for controlling the terminal from scripts and other tools.
|
||||
|
||||
## Installation
|
||||
|
||||
The CLI is bundled with cmuxterm. Inside cmuxterm terminals, it's available automatically. For external use:
|
||||
The CLI is bundled with cmux. Inside cmux terminals, it's available automatically. For external use:
|
||||
|
||||
```bash
|
||||
# Create symlink to CLI
|
||||
sudo ln -sf "/Applications/cmuxterm.app/Contents/MacOS/cmuxterm" /usr/local/bin/cmuxterm
|
||||
sudo ln -sf "/Applications/cmux.app/Contents/MacOS/cmux" /usr/local/bin/cmux
|
||||
```
|
||||
|
||||
## Global Options
|
||||
|
|
@ -42,8 +42,8 @@ sudo ln -sf "/Applications/cmuxterm.app/Contents/MacOS/cmuxterm" /usr/local/bin/
|
|||
List all open tabs.
|
||||
|
||||
```bash
|
||||
cmuxterm list-tabs
|
||||
cmuxterm list-tabs --json
|
||||
cmux list-tabs
|
||||
cmux list-tabs --json
|
||||
```
|
||||
|
||||
#### new-tab
|
||||
|
|
@ -51,7 +51,7 @@ cmuxterm list-tabs --json
|
|||
Create a new tab.
|
||||
|
||||
```bash
|
||||
cmuxterm new-tab
|
||||
cmux new-tab
|
||||
```
|
||||
|
||||
#### select-tab
|
||||
|
|
@ -59,7 +59,7 @@ cmuxterm new-tab
|
|||
Switch to a specific tab.
|
||||
|
||||
```bash
|
||||
cmuxterm select-tab --tab <tab-id>
|
||||
cmux select-tab --tab <tab-id>
|
||||
```
|
||||
|
||||
#### current-tab
|
||||
|
|
@ -67,8 +67,8 @@ cmuxterm select-tab --tab <tab-id>
|
|||
Get the current tab info.
|
||||
|
||||
```bash
|
||||
cmuxterm current-tab
|
||||
cmuxterm current-tab --json
|
||||
cmux current-tab
|
||||
cmux current-tab --json
|
||||
```
|
||||
|
||||
#### close-tab
|
||||
|
|
@ -76,7 +76,7 @@ cmuxterm current-tab --json
|
|||
Close a tab.
|
||||
|
||||
```bash
|
||||
cmuxterm close-tab --tab <tab-id>
|
||||
cmux close-tab --tab <tab-id>
|
||||
```
|
||||
|
||||
### Split Management
|
||||
|
|
@ -86,10 +86,10 @@ cmuxterm close-tab --tab <tab-id>
|
|||
Create a new split pane.
|
||||
|
||||
```bash
|
||||
cmuxterm new-split right
|
||||
cmuxterm new-split down
|
||||
cmuxterm new-split left
|
||||
cmuxterm new-split up
|
||||
cmux new-split right
|
||||
cmux new-split down
|
||||
cmux new-split left
|
||||
cmux new-split up
|
||||
```
|
||||
|
||||
#### list-panels
|
||||
|
|
@ -97,8 +97,8 @@ cmuxterm new-split up
|
|||
List panels in the current tab.
|
||||
|
||||
```bash
|
||||
cmuxterm list-panels
|
||||
cmuxterm list-panels --json
|
||||
cmux list-panels
|
||||
cmux list-panels --json
|
||||
```
|
||||
|
||||
#### focus-panel
|
||||
|
|
@ -106,7 +106,7 @@ cmuxterm list-panels --json
|
|||
Focus a specific panel.
|
||||
|
||||
```bash
|
||||
cmuxterm focus-panel --panel <panel-id>
|
||||
cmux focus-panel --panel <panel-id>
|
||||
```
|
||||
|
||||
### Input Commands
|
||||
|
|
@ -116,8 +116,8 @@ cmuxterm focus-panel --panel <panel-id>
|
|||
Send text to the terminal.
|
||||
|
||||
```bash
|
||||
cmuxterm send "echo hello"
|
||||
cmuxterm send "ls -la\n" # Include newline to execute
|
||||
cmux send "echo hello"
|
||||
cmux send "ls -la\n" # Include newline to execute
|
||||
```
|
||||
|
||||
#### send-key
|
||||
|
|
@ -125,9 +125,9 @@ cmuxterm send "ls -la\n" # Include newline to execute
|
|||
Send a key press.
|
||||
|
||||
```bash
|
||||
cmuxterm send-key enter
|
||||
cmuxterm send-key tab
|
||||
cmuxterm send-key escape
|
||||
cmux send-key enter
|
||||
cmux send-key tab
|
||||
cmux send-key escape
|
||||
```
|
||||
|
||||
#### send-panel
|
||||
|
|
@ -135,7 +135,7 @@ cmuxterm send-key escape
|
|||
Send text to a specific panel.
|
||||
|
||||
```bash
|
||||
cmuxterm send-panel --panel <panel-id> "command"
|
||||
cmux send-panel --panel <panel-id> "command"
|
||||
```
|
||||
|
||||
#### send-key-panel
|
||||
|
|
@ -143,7 +143,7 @@ cmuxterm send-panel --panel <panel-id> "command"
|
|||
Send a key press to a specific panel.
|
||||
|
||||
```bash
|
||||
cmuxterm send-key-panel --panel <panel-id> enter
|
||||
cmux send-key-panel --panel <panel-id> enter
|
||||
```
|
||||
|
||||
### Notifications
|
||||
|
|
@ -153,8 +153,8 @@ cmuxterm send-key-panel --panel <panel-id> enter
|
|||
Send a notification.
|
||||
|
||||
```bash
|
||||
cmuxterm notify --title "Title" --body "Message body"
|
||||
cmuxterm notify --title "Title" --subtitle "Subtitle" --body "Body"
|
||||
cmux notify --title "Title" --body "Message body"
|
||||
cmux notify --title "Title" --subtitle "Subtitle" --body "Body"
|
||||
```
|
||||
|
||||
#### list-notifications
|
||||
|
|
@ -162,8 +162,8 @@ cmuxterm notify --title "Title" --subtitle "Subtitle" --body "Body"
|
|||
List all notifications.
|
||||
|
||||
```bash
|
||||
cmuxterm list-notifications
|
||||
cmuxterm list-notifications --json
|
||||
cmux list-notifications
|
||||
cmux list-notifications --json
|
||||
```
|
||||
|
||||
#### clear-notifications
|
||||
|
|
@ -171,17 +171,17 @@ cmuxterm list-notifications --json
|
|||
Clear all notifications.
|
||||
|
||||
```bash
|
||||
cmuxterm clear-notifications
|
||||
cmux clear-notifications
|
||||
```
|
||||
|
||||
### Utility
|
||||
|
||||
#### ping
|
||||
|
||||
Check if cmuxterm is running and responsive.
|
||||
Check if cmux is running and responsive.
|
||||
|
||||
```bash
|
||||
cmuxterm ping
|
||||
cmux ping
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
|
@ -192,9 +192,9 @@ cmuxterm ping
|
|||
#!/bin/bash
|
||||
npm run build
|
||||
if [ $? -eq 0 ]; then
|
||||
cmuxterm notify --title "✓ Build Success" --body "Ready to deploy"
|
||||
cmux notify --title "✓ Build Success" --body "Ready to deploy"
|
||||
else
|
||||
cmuxterm notify --title "✗ Build Failed" --body "Check the logs"
|
||||
cmux notify --title "✗ Build Failed" --body "Check the logs"
|
||||
fi
|
||||
```
|
||||
|
||||
|
|
@ -203,14 +203,14 @@ fi
|
|||
```bash
|
||||
#!/bin/bash
|
||||
# Create a new tab
|
||||
result=$(cmuxterm new-tab --json)
|
||||
result=$(cmux new-tab --json)
|
||||
tab_id=$(echo "$result" | jq -r '.id')
|
||||
|
||||
# Select the new tab
|
||||
cmuxterm select-tab --tab "$tab_id"
|
||||
cmux select-tab --tab "$tab_id"
|
||||
|
||||
# Send a command
|
||||
cmuxterm send "npm run dev\n"
|
||||
cmux send "npm run dev\n"
|
||||
```
|
||||
|
||||
### Monitor Multiple Tabs
|
||||
|
|
@ -218,5 +218,5 @@ cmuxterm send "npm run dev\n"
|
|||
```bash
|
||||
#!/bin/bash
|
||||
# List all tabs and their directories
|
||||
cmuxterm list-tabs --json | jq -r '.tabs[] | "\(.title): \(.directory)"'
|
||||
cmux list-tabs --json | jq -r '.tabs[] | "\(.title): \(.directory)"'
|
||||
```
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
---
|
||||
title: Configuration
|
||||
description: Configure cmuxterm appearance and behavior
|
||||
description: Configure cmux appearance and behavior
|
||||
---
|
||||
|
||||
# Configuration
|
||||
|
||||
cmuxterm reads configuration from Ghostty config files, giving you familiar options if you're coming from Ghostty.
|
||||
cmux reads configuration from Ghostty config files, giving you familiar options if you're coming from Ghostty.
|
||||
|
||||
## Config File Locations
|
||||
|
||||
cmuxterm looks for configuration in these locations (in order):
|
||||
cmux looks for configuration in these locations (in order):
|
||||
|
||||
1. `~/.config/ghostty/config`
|
||||
2. `~/Library/Application Support/com.mitchellh.ghostty/config`
|
||||
|
|
@ -76,7 +76,7 @@ working-directory = ~/Projects
|
|||
|
||||
## App Settings
|
||||
|
||||
In-app settings are available via **cmuxterm → Settings** (⌘,):
|
||||
In-app settings are available via **cmux → Settings** (⌘,):
|
||||
|
||||
### Theme Mode
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
---
|
||||
title: Environment Variables
|
||||
description: Environment variables for configuring cmuxterm
|
||||
description: Environment variables for configuring cmux
|
||||
---
|
||||
|
||||
# Environment Variables
|
||||
|
||||
cmuxterm uses environment variables for configuration and integration.
|
||||
cmux uses environment variables for configuration and integration.
|
||||
|
||||
## Socket Control
|
||||
|
||||
|
|
@ -14,10 +14,10 @@ cmuxterm uses environment variables for configuration and integration.
|
|||
Override the default socket path.
|
||||
|
||||
```bash
|
||||
export CMUX_SOCKET_PATH=/custom/path/cmuxterm.sock
|
||||
export CMUX_SOCKET_PATH=/custom/path/cmux.sock
|
||||
```
|
||||
|
||||
Default: `/tmp/cmuxterm.sock` (release) or `/tmp/cmuxterm-debug.sock` (debug)
|
||||
Default: `/tmp/cmux.sock` (release) or `/tmp/cmux-debug.sock` (debug)
|
||||
|
||||
### CMUX_SOCKET_ENABLE
|
||||
|
||||
|
|
@ -48,10 +48,10 @@ Default tab ID for CLI commands.
|
|||
|
||||
```bash
|
||||
export CMUX_TAB_ID=abc123
|
||||
cmuxterm send "hello" # Sends to tab abc123
|
||||
cmux send "hello" # Sends to tab abc123
|
||||
```
|
||||
|
||||
cmuxterm automatically sets this in each terminal session.
|
||||
cmux automatically sets this in each terminal session.
|
||||
|
||||
### CMUX_PANEL_ID
|
||||
|
||||
|
|
@ -59,14 +59,14 @@ Default panel ID for CLI commands.
|
|||
|
||||
```bash
|
||||
export CMUX_PANEL_ID=xyz789
|
||||
cmuxterm send-panel "hello" # Sends to panel xyz789
|
||||
cmux send-panel "hello" # Sends to panel xyz789
|
||||
```
|
||||
|
||||
cmuxterm automatically sets this for each split pane.
|
||||
cmux automatically sets this for each split pane.
|
||||
|
||||
## Terminal Environment
|
||||
|
||||
cmuxterm sets these variables in terminal sessions:
|
||||
cmux sets these variables in terminal sessions:
|
||||
|
||||
### TERM
|
||||
|
||||
|
|
@ -85,7 +85,7 @@ TERM_PROGRAM=ghostty
|
|||
```
|
||||
|
||||
<Callout type="info">
|
||||
cmuxterm sets this to `ghostty` since it's built on Ghostty. Use the socket check to distinguish from regular Ghostty.
|
||||
cmux sets this to `ghostty` since it's built on Ghostty. Use the socket check to distinguish from regular Ghostty.
|
||||
</Callout>
|
||||
|
||||
### GHOSTTY_RESOURCES_DIR
|
||||
|
|
@ -124,25 +124,25 @@ export CMUX_COMMIT=abc1234
|
|||
|
||||
## Detection Script
|
||||
|
||||
Check if running inside cmuxterm:
|
||||
Check if running inside cmux:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
|
||||
is_cmuxterm() {
|
||||
# Check for cmuxterm socket
|
||||
if [ -S "${CMUX_SOCKET_PATH:-/tmp/cmuxterm.sock}" ]; then
|
||||
is_cmux() {
|
||||
# Check for cmux socket
|
||||
if [ -S "${CMUX_SOCKET_PATH:-/tmp/cmux.sock}" ]; then
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
if is_cmuxterm; then
|
||||
echo "Running in cmuxterm"
|
||||
if is_cmux; then
|
||||
echo "Running in cmux"
|
||||
echo "Tab ID: ${CMUX_TAB_ID:-unknown}"
|
||||
echo "Panel ID: ${CMUX_PANEL_ID:-unknown}"
|
||||
else
|
||||
echo "Not in cmuxterm"
|
||||
echo "Not in cmux"
|
||||
fi
|
||||
```
|
||||
|
||||
|
|
@ -151,15 +151,15 @@ fi
|
|||
Add to your `~/.bashrc` or `~/.zshrc`:
|
||||
|
||||
```bash
|
||||
# cmuxterm integration
|
||||
if [ -S "${CMUX_SOCKET_PATH:-/tmp/cmuxterm.sock}" ]; then
|
||||
# We're in cmuxterm
|
||||
# cmux integration
|
||||
if [ -S "${CMUX_SOCKET_PATH:-/tmp/cmux.sock}" ]; then
|
||||
# We're in cmux
|
||||
|
||||
# Function to notify on long commands
|
||||
notify_done() {
|
||||
"$@"
|
||||
local exit_code=$?
|
||||
cmuxterm notify --title "Command Complete" --body "$1"
|
||||
cmux notify --title "Command Complete" --body "$1"
|
||||
return $exit_code
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
---
|
||||
title: Introduction
|
||||
description: cmuxterm - A lightweight native macOS terminal with vertical tabs and notifications for AI coding agents
|
||||
description: cmux - A lightweight native macOS terminal with vertical tabs and notifications for AI coding agents
|
||||
---
|
||||
|
||||
# cmuxterm
|
||||
# cmux
|
||||
|
||||
cmuxterm is a lightweight, native macOS terminal built on Ghostty for managing multiple AI coding agents. It features vertical tabs, a notification panel, and a socket-based control API.
|
||||
cmux is a lightweight, native macOS terminal built on Ghostty for managing multiple AI coding agents. It features vertical tabs, a notification panel, and a socket-based control API.
|
||||
|
||||
## Why cmuxterm?
|
||||
## Why cmux?
|
||||
|
||||
- **Native macOS app** - Built with Swift and AppKit, not Electron. Fast startup, low memory usage, and native look and feel.
|
||||
- **Lightweight** - Small binary, minimal resource footprint. No bundled browser engine.
|
||||
|
|
@ -19,7 +19,7 @@ When running AI coding agents like Claude Code, Codex, or similar tools, you nee
|
|||
- **Know when agents need attention** - Agents waiting for input should notify you
|
||||
- **Automate terminal control** - Programmatically create tabs, send input, and more
|
||||
|
||||
cmuxterm solves these with:
|
||||
cmux solves these with:
|
||||
|
||||
- **Vertical sidebar tabs** - All terminals visible at a glance
|
||||
- **OSC 99/777 notifications** - Desktop alerts when agents need you
|
||||
|
|
@ -28,11 +28,11 @@ cmuxterm solves these with:
|
|||
## Quick Start
|
||||
|
||||
```bash
|
||||
brew tap manaflow-ai/cmuxterm
|
||||
brew install --cask cmuxterm
|
||||
brew tap manaflow-ai/cmux
|
||||
brew install --cask cmux
|
||||
```
|
||||
|
||||
Or [download the DMG](https://github.com/manaflow-ai/cmuxterm/releases/latest/download/cmuxterm-macos.dmg) directly.
|
||||
Or [download the DMG](https://github.com/manaflow-ai/cmux/releases/latest/download/cmux-macos.dmg) directly.
|
||||
|
||||
## Key Features
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
title: Installation
|
||||
description: How to install cmuxterm on macOS
|
||||
description: How to install cmux on macOS
|
||||
---
|
||||
|
||||
# Installation
|
||||
|
|
@ -10,29 +10,29 @@ description: How to install cmuxterm on macOS
|
|||
Install via Homebrew:
|
||||
|
||||
```bash
|
||||
brew tap manaflow-ai/cmuxterm
|
||||
brew install --cask cmuxterm
|
||||
brew tap manaflow-ai/cmux
|
||||
brew install --cask cmux
|
||||
```
|
||||
|
||||
To update later:
|
||||
|
||||
```bash
|
||||
brew upgrade --cask cmuxterm
|
||||
brew upgrade --cask cmux
|
||||
```
|
||||
|
||||
## Manual Download
|
||||
|
||||
Download the latest release from GitHub:
|
||||
|
||||
<a href="https://github.com/manaflow-ai/cmuxterm/releases/latest/download/cmuxterm-macos.dmg">
|
||||
<img src="/macos-badge.png" alt="Download cmuxterm for macOS" width="180" />
|
||||
<a href="https://github.com/manaflow-ai/cmux/releases/latest/download/cmux-macos.dmg">
|
||||
<img src="/macos-badge.png" alt="Download cmux for macOS" width="180" />
|
||||
</a>
|
||||
|
||||
Then:
|
||||
|
||||
1. Open the downloaded `.dmg` file
|
||||
2. Drag **cmuxterm** to your **Applications** folder
|
||||
3. Open cmuxterm from Applications
|
||||
2. Drag **cmux** to your **Applications** folder
|
||||
3. Open cmux from Applications
|
||||
|
||||
<Callout type="info">
|
||||
On first launch, macOS may ask you to confirm opening an app from an identified developer. Click **Open** to proceed.
|
||||
|
|
@ -40,7 +40,7 @@ Then:
|
|||
|
||||
## Verify Installation
|
||||
|
||||
Open cmuxterm and you should see:
|
||||
Open cmux and you should see:
|
||||
|
||||
- A terminal window with a vertical tab sidebar on the left
|
||||
- One initial tab already open
|
||||
|
|
@ -48,23 +48,23 @@ Open cmuxterm and you should see:
|
|||
|
||||
## CLI Tool
|
||||
|
||||
cmuxterm includes a command-line tool for automation. It's bundled inside the app and works automatically when you run commands inside cmuxterm.
|
||||
cmux includes a command-line tool for automation. It's bundled inside the app and works automatically when you run commands inside cmux.
|
||||
|
||||
To use the CLI from outside cmuxterm, you can create a symlink:
|
||||
To use the CLI from outside cmux, you can create a symlink:
|
||||
|
||||
```bash
|
||||
sudo ln -sf "/Applications/cmuxterm.app/Contents/MacOS/cmuxterm" /usr/local/bin/cmuxterm
|
||||
sudo ln -sf "/Applications/cmux.app/Contents/MacOS/cmux" /usr/local/bin/cmux
|
||||
```
|
||||
|
||||
Then you can run commands like:
|
||||
|
||||
```bash
|
||||
cmuxterm list-tabs
|
||||
cmuxterm notify --title "Build Complete" --body "Your build finished successfully"
|
||||
cmux list-tabs
|
||||
cmux notify --title "Build Complete" --body "Your build finished successfully"
|
||||
```
|
||||
|
||||
## Updates
|
||||
|
||||
cmuxterm checks for updates automatically using Sparkle. When an update is available, you'll see an update pill in the titlebar. Click it to install the latest version.
|
||||
cmux checks for updates automatically using Sparkle. When an update is available, you'll see an update pill in the titlebar. Click it to install the latest version.
|
||||
|
||||
You can also check for updates manually via **cmuxterm → Check for Updates** in the menu bar.
|
||||
You can also check for updates manually via **cmux → Check for Updates** in the menu bar.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
title: Keyboard Shortcuts
|
||||
description: Complete list of cmuxterm keyboard shortcuts
|
||||
description: Complete list of cmux keyboard shortcuts
|
||||
---
|
||||
|
||||
# Keyboard Shortcuts
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
---
|
||||
title: Notifications
|
||||
description: Desktop notifications in cmuxterm for AI agents
|
||||
description: Desktop notifications in cmux for AI agents
|
||||
---
|
||||
|
||||
# Notifications
|
||||
|
||||
cmuxterm supports desktop notifications, allowing AI agents and scripts to alert you when they need attention.
|
||||
cmux supports desktop notifications, allowing AI agents and scripts to alert you when they need attention.
|
||||
|
||||
## How It Works
|
||||
|
||||
1. A process in the terminal sends an OSC escape sequence
|
||||
2. cmuxterm parses the notification title and body
|
||||
2. cmux parses the notification title and body
|
||||
3. If the terminal isn't focused, a desktop notification appears
|
||||
4. The tab shows an unread badge
|
||||
|
||||
|
|
@ -21,13 +21,13 @@ cmuxterm supports desktop notifications, allowing AI agents and scripts to alert
|
|||
The simplest way to send a notification:
|
||||
|
||||
```bash
|
||||
cmuxterm notify --title "Task Complete" --body "Your build finished"
|
||||
cmux notify --title "Task Complete" --body "Your build finished"
|
||||
```
|
||||
|
||||
With a subtitle:
|
||||
|
||||
```bash
|
||||
cmuxterm notify --title "Claude Code" --subtitle "Waiting" --body "Agent needs input"
|
||||
cmux notify --title "Claude Code" --subtitle "Waiting" --body "Agent needs input"
|
||||
```
|
||||
|
||||
### Using OSC Sequences
|
||||
|
|
@ -59,7 +59,7 @@ See [OSC Sequences](/docs/osc-sequences) for full format documentation.
|
|||
|
||||
Notifications are suppressed (no desktop alert) when:
|
||||
|
||||
- The cmuxterm window is focused
|
||||
- The cmux window is focused
|
||||
- The specific tab sending the notification is active
|
||||
- The notification panel is open
|
||||
|
||||
|
|
@ -94,9 +94,9 @@ notify-after() {
|
|||
"$@"
|
||||
local exit_code=$?
|
||||
if [ $exit_code -eq 0 ]; then
|
||||
cmuxterm notify --title "✓ Command Complete" --body "$1"
|
||||
cmux notify --title "✓ Command Complete" --body "$1"
|
||||
else
|
||||
cmuxterm notify --title "✗ Command Failed" --body "$1 (exit $exit_code)"
|
||||
cmux notify --title "✗ Command Failed" --body "$1 (exit $exit_code)"
|
||||
fi
|
||||
return $exit_code
|
||||
}
|
||||
|
|
@ -108,7 +108,7 @@ notify-after npm run build
|
|||
### Notify When Build Finishes
|
||||
|
||||
```bash
|
||||
npm run build && cmuxterm notify --title "Build Success" --body "Ready to deploy"
|
||||
npm run build && cmux notify --title "Build Success" --body "Ready to deploy"
|
||||
```
|
||||
|
||||
### Watch Script Completion
|
||||
|
|
@ -123,5 +123,5 @@ do_work() {
|
|||
}
|
||||
|
||||
do_work
|
||||
cmuxterm notify --title "Task Complete" --body "long-running-task.sh finished"
|
||||
cmux notify --title "Task Complete" --body "long-running-task.sh finished"
|
||||
```
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
---
|
||||
title: OSC Sequences
|
||||
description: OSC escape sequence reference for cmuxterm notifications
|
||||
description: OSC escape sequence reference for cmux notifications
|
||||
---
|
||||
|
||||
# OSC Sequences
|
||||
|
||||
cmuxterm supports two OSC (Operating System Command) escape sequence standards for sending notifications from terminal programs.
|
||||
cmux supports two OSC (Operating System Command) escape sequence standards for sending notifications from terminal programs.
|
||||
|
||||
## OSC 99 (Kitty Protocol)
|
||||
|
||||
|
|
@ -137,13 +137,13 @@ notify_osc777 "Download Complete" "File saved to ~/Downloads"
|
|||
| Subtitle | ✓ | ✗ |
|
||||
| Notification ID | ✓ | ✗ |
|
||||
| Complexity | Higher | Lower |
|
||||
| Compatibility | Kitty, cmuxterm | RXVT, cmuxterm |
|
||||
| Compatibility | Kitty, cmux | RXVT, cmux |
|
||||
|
||||
## Recommendations
|
||||
|
||||
- **Use OSC 777** for simple title + body notifications
|
||||
- **Use OSC 99** when you need subtitles or notification IDs
|
||||
- **Use the CLI** (`cmuxterm notify`) for the easiest integration
|
||||
- **Use the CLI** (`cmux notify`) for the easiest integration
|
||||
|
||||
## Testing
|
||||
|
||||
|
|
@ -163,7 +163,7 @@ printf '\e]99;i=test;e=1;d=1;p=body:This is the body text\e\\'
|
|||
|
||||
### tmux
|
||||
|
||||
If using tmux inside cmuxterm, enable passthrough:
|
||||
If using tmux inside cmux, enable passthrough:
|
||||
|
||||
```bash
|
||||
# In .tmux.conf
|
||||
|
|
|
|||
|
|
@ -1,24 +1,24 @@
|
|||
---
|
||||
title: Socket API
|
||||
description: Unix socket API for programmatic control of cmuxterm
|
||||
description: Unix socket API for programmatic control of cmux
|
||||
---
|
||||
|
||||
# Socket API
|
||||
|
||||
cmuxterm exposes a Unix socket for programmatic control, enabling automation and integration with tools like Claude Code.
|
||||
cmux exposes a Unix socket for programmatic control, enabling automation and integration with tools like Claude Code.
|
||||
|
||||
## Socket Location
|
||||
|
||||
| Build | Socket Path |
|
||||
|-------|-------------|
|
||||
| Release | `/tmp/cmuxterm.sock` |
|
||||
| Debug | `/tmp/cmuxterm-debug.sock` |
|
||||
| Release | `/tmp/cmux.sock` |
|
||||
| Debug | `/tmp/cmux-debug.sock` |
|
||||
|
||||
You can override the path with the `CMUX_SOCKET_PATH` environment variable.
|
||||
|
||||
## Access Modes
|
||||
|
||||
cmuxterm has three access modes, configurable in Settings:
|
||||
cmux has three access modes, configurable in Settings:
|
||||
|
||||
| Mode | Description |
|
||||
|------|-------------|
|
||||
|
|
@ -227,7 +227,7 @@ import json
|
|||
|
||||
def send_command(cmd):
|
||||
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
sock.connect('/tmp/cmuxterm.sock')
|
||||
sock.connect('/tmp/cmux.sock')
|
||||
sock.send(json.dumps(cmd).encode() + b'\n')
|
||||
response = sock.recv(4096).decode()
|
||||
sock.close()
|
||||
|
|
@ -252,7 +252,7 @@ send_command({
|
|||
|
||||
# Function to send commands
|
||||
cmux_cmd() {
|
||||
echo "$1" | nc -U /tmp/cmuxterm.sock
|
||||
echo "$1" | nc -U /tmp/cmux.sock
|
||||
}
|
||||
|
||||
# List tabs
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
---
|
||||
title: Split Panes
|
||||
description: Working with split panes in cmuxterm
|
||||
description: Working with split panes in cmux
|
||||
---
|
||||
|
||||
# Split Panes
|
||||
|
||||
cmuxterm supports splitting terminal panes within a tab, allowing you to view multiple terminals side by side.
|
||||
cmux supports splitting terminal panes within a tab, allowing you to view multiple terminals side by side.
|
||||
|
||||
## Creating Splits
|
||||
|
||||
|
|
@ -20,10 +20,10 @@ cmuxterm supports splitting terminal panes within a tab, allowing you to view mu
|
|||
|
||||
```bash
|
||||
# Split in a specific direction
|
||||
cmuxterm new-split right
|
||||
cmuxterm new-split down
|
||||
cmuxterm new-split left
|
||||
cmuxterm new-split up
|
||||
cmux new-split right
|
||||
cmux new-split down
|
||||
cmux new-split left
|
||||
cmux new-split up
|
||||
```
|
||||
|
||||
## Navigating Splits
|
||||
|
|
@ -43,10 +43,10 @@ Use **⌘⌥** + arrow keys to move between splits:
|
|||
|
||||
```bash
|
||||
# List all panes in current tab
|
||||
cmuxterm list-panels
|
||||
cmux list-panels
|
||||
|
||||
# Focus a specific pane
|
||||
cmuxterm focus-panel --panel <panel-id>
|
||||
cmux focus-panel --panel <panel-id>
|
||||
```
|
||||
|
||||
## Closing Splits
|
||||
|
|
@ -75,7 +75,7 @@ split-divider-color = #45475a
|
|||
|
||||
## Split Layout
|
||||
|
||||
cmuxterm uses a binary split tree structure:
|
||||
cmux uses a binary split tree structure:
|
||||
|
||||
- Each split creates two child panes
|
||||
- Nested splits create a tree of panes
|
||||
|
|
@ -104,8 +104,8 @@ With the CLI, you can send input to specific panes:
|
|||
|
||||
```bash
|
||||
# Send text to a specific pane
|
||||
cmuxterm send-panel --panel <panel-id> "echo hello"
|
||||
cmux send-panel --panel <panel-id> "echo hello"
|
||||
|
||||
# Send a keypress to a specific pane
|
||||
cmuxterm send-key-panel --panel <panel-id> enter
|
||||
cmux send-key-panel --panel <panel-id> enter
|
||||
```
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
---
|
||||
title: Vertical Tabs
|
||||
description: Managing terminal tabs in cmuxterm
|
||||
description: Managing terminal tabs in cmux
|
||||
---
|
||||
|
||||
# Vertical Tabs
|
||||
|
||||
cmuxterm displays all your terminal tabs in a vertical sidebar on the left side of the window, making it easy to see and switch between multiple terminals at a glance.
|
||||
cmux displays all your terminal tabs in a vertical sidebar on the left side of the window, making it easy to see and switch between multiple terminals at a glance.
|
||||
|
||||
## Sidebar Features
|
||||
|
||||
|
|
|
|||
4
docs-site/package-lock.json
generated
4
docs-site/package-lock.json
generated
|
|
@ -1,11 +1,11 @@
|
|||
{
|
||||
"name": "cmuxterm-docs",
|
||||
"name": "cmux-docs",
|
||||
"version": "0.1.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "cmuxterm-docs",
|
||||
"name": "cmux-docs",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"fumadocs-core": "^14.0.0",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name": "cmuxterm-docs",
|
||||
"name": "cmux-docs",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
|
|
|||
|
|
@ -1,29 +1,29 @@
|
|||
# Notifications
|
||||
|
||||
cmuxterm provides a notification panel for AI agents like Claude Code, Codex, and OpenCode. Notifications appear in a dedicated panel and trigger macOS system notifications.
|
||||
cmux provides a notification panel for AI agents like Claude Code, Codex, and OpenCode. Notifications appear in a dedicated panel and trigger macOS system notifications.
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
# Send a notification (if cmuxterm is available)
|
||||
command -v cmuxterm &>/dev/null && cmuxterm notify --title "Done" --body "Task complete"
|
||||
# Send a notification (if cmux is available)
|
||||
command -v cmux &>/dev/null && cmux notify --title "Done" --body "Task complete"
|
||||
|
||||
# With fallback to macOS notifications
|
||||
command -v cmuxterm &>/dev/null && cmuxterm notify --title "Done" --body "Task complete" || osascript -e 'display notification "Task complete" with title "Done"'
|
||||
command -v cmux &>/dev/null && cmux notify --title "Done" --body "Task complete" || osascript -e 'display notification "Task complete" with title "Done"'
|
||||
```
|
||||
|
||||
## Detection
|
||||
|
||||
Check if `cmuxterm` CLI is available before using it:
|
||||
Check if `cmux` CLI is available before using it:
|
||||
|
||||
```bash
|
||||
# Shell
|
||||
if command -v cmuxterm &>/dev/null; then
|
||||
cmuxterm notify --title "Hello"
|
||||
if command -v cmux &>/dev/null; then
|
||||
cmux notify --title "Hello"
|
||||
fi
|
||||
|
||||
# One-liner with fallback
|
||||
command -v cmuxterm &>/dev/null && cmuxterm notify --title "Hello" || osascript -e 'display notification "" with title "Hello"'
|
||||
command -v cmux &>/dev/null && cmux notify --title "Hello" || osascript -e 'display notification "" with title "Hello"'
|
||||
```
|
||||
|
||||
```python
|
||||
|
|
@ -32,8 +32,8 @@ import shutil
|
|||
import subprocess
|
||||
|
||||
def notify(title: str, body: str = ""):
|
||||
if shutil.which("cmuxterm"):
|
||||
subprocess.run(["cmuxterm", "notify", "--title", title, "--body", body])
|
||||
if shutil.which("cmux"):
|
||||
subprocess.run(["cmux", "notify", "--title", title, "--body", body])
|
||||
else:
|
||||
# Fallback to macOS
|
||||
subprocess.run(["osascript", "-e", f'display notification "{body}" with title "{title}"'])
|
||||
|
|
@ -43,13 +43,13 @@ def notify(title: str, body: str = ""):
|
|||
|
||||
```bash
|
||||
# Simple notification
|
||||
cmuxterm notify --title "Build Complete"
|
||||
cmux notify --title "Build Complete"
|
||||
|
||||
# With subtitle and body
|
||||
cmuxterm notify --title "Claude Code" --subtitle "Permission" --body "Approval needed"
|
||||
cmux notify --title "Claude Code" --subtitle "Permission" --body "Approval needed"
|
||||
|
||||
# Notify specific tab/panel
|
||||
cmuxterm notify --title "Done" --tab 0 --panel 1
|
||||
cmux notify --title "Done" --tab 0 --panel 1
|
||||
```
|
||||
|
||||
## Integration Examples
|
||||
|
|
@ -67,7 +67,7 @@ Add to `~/.claude/settings.json`:
|
|||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "command -v cmuxterm &>/dev/null && cmuxterm notify --title 'Claude Code' --body 'Waiting for input' || osascript -e 'display notification \"Waiting for input\" with title \"Claude Code\"'"
|
||||
"command": "command -v cmux &>/dev/null && cmux notify --title 'Claude Code' --body 'Waiting for input' || osascript -e 'display notification \"Waiting for input\" with title \"Claude Code\"'"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -76,7 +76,7 @@ Add to `~/.claude/settings.json`:
|
|||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "command -v cmuxterm &>/dev/null && cmuxterm notify --title 'Claude Code' --subtitle 'Permission' --body 'Approval needed' || osascript -e 'display notification \"Approval needed\" with title \"Claude Code\"'"
|
||||
"command": "command -v cmux &>/dev/null && cmux notify --title 'Claude Code' --subtitle 'Permission' --body 'Approval needed' || osascript -e 'display notification \"Approval needed\" with title \"Claude Code\"'"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -90,7 +90,7 @@ Add to `~/.claude/settings.json`:
|
|||
Add to `~/.codex/config.toml`:
|
||||
|
||||
```toml
|
||||
notify = ["bash", "-c", "command -v cmuxterm &>/dev/null && cmuxterm notify --title Codex --body \"$(echo $1 | jq -r '.\"last-assistant-message\" // \"Turn complete\"' 2>/dev/null | head -c 100)\" || osascript -e 'display notification \"Turn complete\" with title \"Codex\"'", "--"]
|
||||
notify = ["bash", "-c", "command -v cmux &>/dev/null && cmux notify --title Codex --body \"$(echo $1 | jq -r '.\"last-assistant-message\" // \"Turn complete\"' 2>/dev/null | head -c 100)\" || osascript -e 'display notification \"Turn complete\" with title \"Codex\"'", "--"]
|
||||
```
|
||||
|
||||
Or create a simple script `~/.local/bin/codex-notify.sh`:
|
||||
|
|
@ -98,7 +98,7 @@ Or create a simple script `~/.local/bin/codex-notify.sh`:
|
|||
```bash
|
||||
#!/bin/bash
|
||||
MSG=$(echo "$1" | jq -r '."last-assistant-message" // "Turn complete"' 2>/dev/null | head -c 100)
|
||||
command -v cmuxterm &>/dev/null && cmuxterm notify --title "Codex" --body "$MSG" || osascript -e "display notification \"$MSG\" with title \"Codex\""
|
||||
command -v cmux &>/dev/null && cmux notify --title "Codex" --body "$MSG" || osascript -e "display notification \"$MSG\" with title \"Codex\""
|
||||
```
|
||||
|
||||
Then use:
|
||||
|
|
@ -108,13 +108,13 @@ notify = ["bash", "~/.local/bin/codex-notify.sh"]
|
|||
|
||||
### OpenCode Plugin
|
||||
|
||||
Create `.opencode/plugins/cmuxterm-notify.js`:
|
||||
Create `.opencode/plugins/cmux-notify.js`:
|
||||
|
||||
```javascript
|
||||
export const CmuxNotificationPlugin = async ({ $, }) => {
|
||||
const notify = async (title, body) => {
|
||||
try {
|
||||
await $`command -v cmuxterm && cmuxterm notify --title ${title} --body ${body}`;
|
||||
await $`command -v cmux && cmux notify --title ${title} --body ${body}`;
|
||||
} catch {
|
||||
await $`osascript -e ${"display notification \"" + body + "\" with title \"" + title + "\""}`;
|
||||
}
|
||||
|
|
@ -132,7 +132,7 @@ export const CmuxNotificationPlugin = async ({ $, }) => {
|
|||
|
||||
## Environment Variables
|
||||
|
||||
cmuxterm sets these in child shells:
|
||||
cmux sets these in child shells:
|
||||
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
|
|
@ -143,14 +143,14 @@ cmuxterm sets these in child shells:
|
|||
## CLI Commands
|
||||
|
||||
```
|
||||
cmuxterm notify --title <text> [--subtitle <text>] [--body <text>] [--tab <id|index>] [--panel <id|index>]
|
||||
cmuxterm list-notifications
|
||||
cmuxterm clear-notifications
|
||||
cmuxterm ping
|
||||
cmux notify --title <text> [--subtitle <text>] [--body <text>] [--tab <id|index>] [--panel <id|index>]
|
||||
cmux list-notifications
|
||||
cmux clear-notifications
|
||||
cmux ping
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Always check availability first** - Use `command -v cmuxterm` before calling
|
||||
1. **Always check availability first** - Use `command -v cmux` before calling
|
||||
2. **Provide fallbacks** - Use `|| osascript` for macOS fallback
|
||||
3. **Keep notifications concise** - Title should be brief, use body for details
|
||||
|
|
|
|||
1
homebrew-cmux
Submodule
1
homebrew-cmux
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 19af91f6d47016a74a08546e8693dc03eefb7ef5
|
||||
|
|
@ -1 +0,0 @@
|
|||
Subproject commit c12d753824b3ab1f4289a7e45d7ea28aa114b496
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
APP_NAME="cmuxterm DEV"
|
||||
BUNDLE_ID="com.cmuxterm.app.debug"
|
||||
BASE_APP_NAME="cmuxterm DEV"
|
||||
APP_NAME="cmux DEV"
|
||||
BUNDLE_ID="com.cmux.app.debug"
|
||||
BASE_APP_NAME="cmux DEV"
|
||||
DERIVED_DATA=""
|
||||
NAME_SET=0
|
||||
BUNDLE_SET=0
|
||||
|
|
@ -97,13 +97,13 @@ if [[ -n "$TAG" ]]; then
|
|||
TAG_ID="$(sanitize_bundle "$TAG")"
|
||||
TAG_SLUG="$(sanitize_path "$TAG")"
|
||||
if [[ "$NAME_SET" -eq 0 ]]; then
|
||||
APP_NAME="cmuxterm DEV ${TAG}"
|
||||
APP_NAME="cmux DEV ${TAG}"
|
||||
fi
|
||||
if [[ "$BUNDLE_SET" -eq 0 ]]; then
|
||||
BUNDLE_ID="com.cmuxterm.app.debug.${TAG_ID}"
|
||||
BUNDLE_ID="com.cmux.app.debug.${TAG_ID}"
|
||||
fi
|
||||
if [[ "$DERIVED_SET" -eq 0 ]]; then
|
||||
DERIVED_DATA="/tmp/cmuxterm-${TAG_SLUG}"
|
||||
DERIVED_DATA="/tmp/cmux-${TAG_SLUG}"
|
||||
fi
|
||||
fi
|
||||
|
||||
|
|
@ -180,10 +180,10 @@ if [[ -n "$TAG" && "$APP_NAME" != "$SEARCH_APP_NAME" ]]; then
|
|||
/usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier $BUNDLE_ID" "$INFO_PLIST" 2>/dev/null \
|
||||
|| /usr/libexec/PlistBuddy -c "Add :CFBundleIdentifier string $BUNDLE_ID" "$INFO_PLIST"
|
||||
if [[ -n "${TAG_SLUG:-}" ]]; then
|
||||
APP_SUPPORT_DIR="$HOME/Library/Application Support/cmuxterm"
|
||||
APP_SUPPORT_DIR="$HOME/Library/Application Support/cmux"
|
||||
CMUXD_SOCKET="${APP_SUPPORT_DIR}/cmuxd-dev-${TAG_SLUG}.sock"
|
||||
CMUX_SOCKET="/tmp/cmuxterm-debug-${TAG_SLUG}.sock"
|
||||
echo "$CMUX_SOCKET" > /tmp/cmuxterm-last-socket-path || true
|
||||
CMUX_SOCKET="/tmp/cmux-debug-${TAG_SLUG}.sock"
|
||||
echo "$CMUX_SOCKET" > /tmp/cmux-last-socket-path || true
|
||||
/usr/libexec/PlistBuddy -c "Add :LSEnvironment dict" "$INFO_PLIST" 2>/dev/null || true
|
||||
/usr/libexec/PlistBuddy -c "Set :LSEnvironment:CMUXD_UNIX_PATH \"${CMUXD_SOCKET}\"" "$INFO_PLIST" 2>/dev/null \
|
||||
|| /usr/libexec/PlistBuddy -c "Add :LSEnvironment:CMUXD_UNIX_PATH string \"${CMUXD_SOCKET}\"" "$INFO_PLIST"
|
||||
|
|
@ -225,8 +225,8 @@ if [[ -x "$CMUXD_SRC" ]]; then
|
|||
cp "$CMUXD_SRC" "$BIN_DIR/cmuxd"
|
||||
chmod +x "$BIN_DIR/cmuxd"
|
||||
fi
|
||||
# Avoid inheriting cmuxterm/ghostty environment variables from the terminal that
|
||||
# runs this script (often inside another cmuxterm instance), which can cause
|
||||
# Avoid inheriting cmux/ghostty environment variables from the terminal that
|
||||
# runs this script (often inside another cmux instance), which can cause
|
||||
# socket and resource-path conflicts.
|
||||
OPEN_CLEAN_ENV=(
|
||||
env
|
||||
|
|
@ -235,15 +235,13 @@ OPEN_CLEAN_ENV=(
|
|||
-u CMUX_PANEL_ID
|
||||
-u CMUXD_UNIX_PATH
|
||||
-u CMUX_TAG
|
||||
-u CMUXTERM_TAG
|
||||
-u CMUX_BUNDLE_ID
|
||||
-u CMUXTERM_BUNDLE_ID
|
||||
-u CMUX_SHELL_INTEGRATION
|
||||
-u GHOSTTY_BIN_DIR
|
||||
-u GHOSTTY_RESOURCES_DIR
|
||||
-u GHOSTTY_SHELL_FEATURES
|
||||
# Dev shells (including CI/Codex) often force-disable paging by exporting these.
|
||||
# Don't leak that into cmuxterm, otherwise `git diff` won't page even with PAGER=less.
|
||||
# Don't leak that into cmux, otherwise `git diff` won't page even with PAGER=less.
|
||||
-u GIT_PAGER
|
||||
-u GH_PAGER
|
||||
-u TERMINFO
|
||||
|
|
|
|||
|
|
@ -2,20 +2,20 @@
|
|||
set -euo pipefail
|
||||
|
||||
xcodebuild -project GhosttyTabs.xcodeproj -scheme cmux -configuration Release -destination 'platform=macOS' build
|
||||
pkill -x cmuxterm || true
|
||||
pkill -x cmux || true
|
||||
sleep 0.2
|
||||
APP_PATH="$(
|
||||
find "$HOME/Library/Developer/Xcode/DerivedData" -path "*/Build/Products/Release/cmuxterm.app" -print0 \
|
||||
find "$HOME/Library/Developer/Xcode/DerivedData" -path "*/Build/Products/Release/cmux.app" -print0 \
|
||||
| xargs -0 /usr/bin/stat -f "%m %N" 2>/dev/null \
|
||||
| sort -nr \
|
||||
| head -n 1 \
|
||||
| cut -d' ' -f2-
|
||||
)"
|
||||
if [[ -z "${APP_PATH}" ]]; then
|
||||
echo "cmuxterm.app not found in DerivedData" >&2
|
||||
echo "cmux.app not found in DerivedData" >&2
|
||||
exit 1
|
||||
fi
|
||||
# Dev shells (including CI/Codex) often force-disable paging by exporting these.
|
||||
# Don't leak that into cmuxterm, otherwise `git diff` won't page even with PAGER=less.
|
||||
# Don't leak that into cmux, otherwise `git diff` won't page even with PAGER=less.
|
||||
env -u GIT_PAGER -u GH_PAGER open "$APP_PATH"
|
||||
osascript -e 'tell application "cmuxterm" to activate' || true
|
||||
osascript -e 'tell application "cmux" to activate' || true
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ if [[ -z "${SPARKLE_PRIVATE_KEY:-}" ]]; then
|
|||
fi
|
||||
|
||||
SPARKLE_VERSION="${SPARKLE_VERSION:-2.8.1}"
|
||||
DOWNLOAD_URL_PREFIX="${DOWNLOAD_URL_PREFIX:-https://github.com/manaflow-ai/cmuxterm/releases/download/$TAG/}"
|
||||
RELEASE_NOTES_URL="${RELEASE_NOTES_URL:-https://github.com/manaflow-ai/cmuxterm/releases/tag/$TAG}"
|
||||
DOWNLOAD_URL_PREFIX="${DOWNLOAD_URL_PREFIX:-https://github.com/manaflow-ai/cmux/releases/download/$TAG/}"
|
||||
RELEASE_NOTES_URL="${RELEASE_NOTES_URL:-https://github.com/manaflow-ai/cmux/releases/tag/$TAG}"
|
||||
|
||||
work_dir="$(mktemp -d)"
|
||||
cleanup() {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
set -euo pipefail
|
||||
|
||||
SPARKLE_VERSION="${SPARKLE_VERSION:-2.8.1}"
|
||||
SPARKLE_KEYCHAIN_ACCOUNT="${SPARKLE_KEYCHAIN_ACCOUNT:-cmuxterm}"
|
||||
SPARKLE_KEYCHAIN_ACCOUNT="${SPARKLE_KEYCHAIN_ACCOUNT:-cmux}"
|
||||
SPARKLE_ENV_FILE="${SPARKLE_ENV_FILE:-.env}"
|
||||
|
||||
work_dir="$(mktemp -d)"
|
||||
|
|
|
|||
|
|
@ -43,8 +43,8 @@ class cmuxError(Exception):
|
|||
pass
|
||||
|
||||
|
||||
_LAST_SOCKET_PATH_FILE = "/tmp/cmuxterm-last-socket-path"
|
||||
_DEFAULT_DEBUG_BUNDLE_ID = "com.cmuxterm.app.debug"
|
||||
_LAST_SOCKET_PATH_FILE = "/tmp/cmux-last-socket-path"
|
||||
_DEFAULT_DEBUG_BUNDLE_ID = "com.cmux.app.debug"
|
||||
|
||||
|
||||
def _sanitize_tag_slug(raw: str) -> str:
|
||||
|
|
@ -68,11 +68,11 @@ def _quote_option_value(value: str) -> str:
|
|||
|
||||
|
||||
def _default_bundle_id() -> str:
|
||||
override = os.environ.get("CMUX_BUNDLE_ID") or os.environ.get("CMUXTERM_BUNDLE_ID")
|
||||
override = os.environ.get("CMUX_BUNDLE_ID")
|
||||
if override:
|
||||
return override
|
||||
|
||||
tag = os.environ.get("CMUX_TAG") or os.environ.get("CMUXTERM_TAG")
|
||||
tag = os.environ.get("CMUX_TAG")
|
||||
if tag:
|
||||
suffix = _sanitize_bundle_suffix(tag)
|
||||
return f"{_DEFAULT_DEBUG_BUNDLE_ID}.{suffix}"
|
||||
|
|
@ -110,12 +110,12 @@ def _can_connect(path: str, timeout: float = 0.15, retries: int = 4) -> bool:
|
|||
|
||||
|
||||
def _default_socket_path() -> str:
|
||||
tag = os.environ.get("CMUX_TAG") or os.environ.get("CMUXTERM_TAG")
|
||||
tag = os.environ.get("CMUX_TAG")
|
||||
if tag:
|
||||
slug = _sanitize_tag_slug(tag)
|
||||
tagged_candidates = [
|
||||
f"/tmp/cmuxterm-debug-{slug}.sock",
|
||||
f"/tmp/cmuxterm-{slug}.sock",
|
||||
f"/tmp/cmux-debug-{slug}.sock",
|
||||
f"/tmp/cmux-{slug}.sock",
|
||||
]
|
||||
for path in tagged_candidates:
|
||||
if os.path.exists(path) and _can_connect(path):
|
||||
|
|
@ -142,13 +142,13 @@ def _default_socket_path() -> str:
|
|||
return last_socket
|
||||
|
||||
# Prefer the non-tagged sockets when present.
|
||||
candidates = ["/tmp/cmuxterm-debug.sock", "/tmp/cmuxterm.sock"]
|
||||
candidates = ["/tmp/cmux-debug.sock", "/tmp/cmux.sock"]
|
||||
for path in candidates:
|
||||
if os.path.exists(path) and _can_connect(path):
|
||||
return path
|
||||
|
||||
# Otherwise, fall back to the newest tagged debug socket if there is one.
|
||||
tagged = glob.glob("/tmp/cmuxterm-debug-*.sock")
|
||||
tagged = glob.glob("/tmp/cmux-debug-*.sock")
|
||||
tagged = [p for p in tagged if os.path.exists(p)]
|
||||
if tagged:
|
||||
tagged.sort(key=lambda p: os.path.getmtime(p), reverse=True)
|
||||
|
|
|
|||
|
|
@ -1,24 +1,24 @@
|
|||
#!/bin/bash
|
||||
# Test script that sends keystrokes to cmuxterm via AppleScript
|
||||
# Test script that sends keystrokes to cmux via AppleScript
|
||||
# This tests the actual keyboard input path through the app
|
||||
|
||||
set -e
|
||||
|
||||
echo "=== cmuxterm Keystroke Test ==="
|
||||
echo "=== cmux Keystroke Test ==="
|
||||
echo ""
|
||||
|
||||
# Check if cmuxterm is running
|
||||
if ! pgrep -x "cmuxterm" > /dev/null; then
|
||||
echo "Error: cmuxterm is not running"
|
||||
echo "Please start cmuxterm first"
|
||||
# Check if cmux is running
|
||||
if ! pgrep -x "cmux" > /dev/null; then
|
||||
echo "Error: cmux is not running"
|
||||
echo "Please start cmux first"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "cmuxterm is running"
|
||||
echo "cmux is running"
|
||||
echo ""
|
||||
|
||||
# Activate cmuxterm
|
||||
osascript -e 'tell application "cmuxterm" to activate'
|
||||
# Activate cmux
|
||||
osascript -e 'tell application "cmux" to activate'
|
||||
sleep 0.5
|
||||
|
||||
echo "Test 1: Testing Ctrl+C (SIGINT)"
|
||||
|
|
@ -56,7 +56,7 @@ echo " If cat exited, Ctrl+D is working!"
|
|||
echo ""
|
||||
|
||||
echo "=== Manual Verification Required ==="
|
||||
echo "Please check the cmuxterm window to verify:"
|
||||
echo "Please check the cmux window to verify:"
|
||||
echo " 1. The 'sleep 30' command was interrupted by Ctrl+C"
|
||||
echo " 2. The 'cat' command exited after Ctrl+D"
|
||||
echo ""
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ Tests that CPU usage stays reasonable when:
|
|||
Usage:
|
||||
python3 tests/test_cpu_notifications.py
|
||||
|
||||
Requires cmuxterm to be running with socket control enabled.
|
||||
Requires cmux to be running with socket control enabled.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
|
@ -39,8 +39,8 @@ SETTLE_TIME = 2.0
|
|||
MONITOR_DURATION = 3.0
|
||||
|
||||
|
||||
def get_cmuxterm_pid() -> Optional[int]:
|
||||
"""Get the PID of the running cmuxterm process."""
|
||||
def get_cmux_pid() -> Optional[int]:
|
||||
"""Get the PID of the running cmux process."""
|
||||
socket_path = os.environ.get("CMUX_SOCKET_PATH")
|
||||
if not socket_path:
|
||||
# Ask cmux.py to resolve default socket path (supports CMUX_TAG and last-socket file).
|
||||
|
|
@ -68,14 +68,14 @@ def get_cmuxterm_pid() -> Optional[int]:
|
|||
return pid
|
||||
|
||||
result = subprocess.run(
|
||||
["pgrep", "-f", r"cmuxterm\.app/Contents/MacOS/cmuxterm$"],
|
||||
["pgrep", "-f", r"cmux\.app/Contents/MacOS/cmux$"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
if result.returncode != 0:
|
||||
# Try DEV build
|
||||
result = subprocess.run(
|
||||
["pgrep", "-f", r"cmuxterm DEV\.app/Contents/MacOS/cmuxterm"],
|
||||
["pgrep", "-f", r"cmux DEV\.app/Contents/MacOS/cmux"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
|
|
@ -167,7 +167,7 @@ def test_cpu_after_popover_close(client: cmux, pid: int) -> tuple[bool, str]:
|
|||
time.sleep(0.1)
|
||||
time.sleep(0.5)
|
||||
|
||||
# Ensure the correct cmuxterm instance is frontmost (tag-safe).
|
||||
# Ensure the correct cmux instance is frontmost (tag-safe).
|
||||
bundle_id = cmux.default_bundle_id()
|
||||
subprocess.run(
|
||||
["osascript", "-e", f'tell application id "{bundle_id}" to activate'],
|
||||
|
|
@ -243,17 +243,17 @@ def test_cpu_idle_with_notifications(client: cmux, pid: int) -> tuple[bool, str]
|
|||
|
||||
def main():
|
||||
print("=" * 60)
|
||||
print("cmuxterm Notification CPU Tests")
|
||||
print("cmux Notification CPU Tests")
|
||||
print("=" * 60)
|
||||
|
||||
socket_path = cmux().socket_path
|
||||
|
||||
pid = get_cmuxterm_pid()
|
||||
pid = get_cmux_pid()
|
||||
if pid is None:
|
||||
print("\n❌ SKIP: cmuxterm is not running")
|
||||
print("\n❌ SKIP: cmux is not running")
|
||||
return 0
|
||||
|
||||
print(f"\nFound cmuxterm process: PID {pid}")
|
||||
print(f"\nFound cmux process: PID {pid}")
|
||||
|
||||
# Try to connect to the socket
|
||||
client = cmux(socket_path)
|
||||
|
|
@ -261,7 +261,7 @@ def main():
|
|||
client.connect()
|
||||
print(f"Connected to {socket_path}")
|
||||
except cmuxError:
|
||||
print("\n❌ SKIP: Could not connect to cmuxterm socket")
|
||||
print("\n❌ SKIP: Could not connect to cmux socket")
|
||||
print("Tip: set CMUX_TAG=<tag> or CMUX_SOCKET_PATH=<path> to target a tagged instance.")
|
||||
return 0
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
CPU usage test for cmuxterm.
|
||||
CPU usage test for cmux.
|
||||
|
||||
This test monitors cmuxterm's CPU usage during idle periods to catch
|
||||
This test monitors cmux's CPU usage during idle periods to catch
|
||||
performance regressions like runaway animations or continuous view updates.
|
||||
|
||||
Run this test after launching cmuxterm:
|
||||
Run this test after launching cmux:
|
||||
python3 tests/test_cpu_usage.py
|
||||
|
||||
The test will fail if:
|
||||
|
|
@ -49,8 +49,8 @@ SUSPICIOUS_PATTERNS = [
|
|||
]
|
||||
|
||||
|
||||
def get_cmuxterm_pid() -> Optional[int]:
|
||||
"""Get the PID of the running cmuxterm process."""
|
||||
def get_cmux_pid() -> Optional[int]:
|
||||
"""Get the PID of the running cmux process."""
|
||||
socket_path = os.environ.get("CMUX_SOCKET_PATH")
|
||||
if not socket_path:
|
||||
try:
|
||||
|
|
@ -77,14 +77,14 @@ def get_cmuxterm_pid() -> Optional[int]:
|
|||
return pid
|
||||
|
||||
result = subprocess.run(
|
||||
["pgrep", "-f", r"cmuxterm\.app/Contents/MacOS/cmuxterm$"],
|
||||
["pgrep", "-f", r"cmux\.app/Contents/MacOS/cmux$"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
if result.returncode != 0:
|
||||
# Try DEV build
|
||||
result = subprocess.run(
|
||||
["pgrep", "-f", r"cmuxterm DEV\.app/Contents/MacOS/cmuxterm"],
|
||||
["pgrep", "-f", r"cmux DEV\.app/Contents/MacOS/cmux"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
|
|
@ -141,17 +141,17 @@ def monitor_cpu_usage(pid: int, duration: float, interval: float) -> List[float]
|
|||
|
||||
def main():
|
||||
print("=" * 60)
|
||||
print("cmuxterm CPU Usage Test")
|
||||
print("cmux CPU Usage Test")
|
||||
print("=" * 60)
|
||||
|
||||
# Find cmuxterm process
|
||||
pid = get_cmuxterm_pid()
|
||||
# Find cmux process
|
||||
pid = get_cmux_pid()
|
||||
if pid is None:
|
||||
print("\n❌ SKIP: cmuxterm is not running")
|
||||
print("Start cmuxterm and run this test again.")
|
||||
print("\n❌ SKIP: cmux is not running")
|
||||
print("Start cmux and run this test again.")
|
||||
return 0 # Not a failure, just skip
|
||||
|
||||
print(f"\nFound cmuxterm process: PID {pid}")
|
||||
print(f"\nFound cmux process: PID {pid}")
|
||||
|
||||
# Wait for app to settle
|
||||
print(f"Waiting {SETTLE_TIME}s for app to settle...")
|
||||
|
|
@ -187,7 +187,7 @@ def main():
|
|||
print(f" - {issue}")
|
||||
|
||||
# Save sample for debugging
|
||||
sample_file = Path(f"/tmp/cmuxterm_cpu_test_sample_{pid}.txt")
|
||||
sample_file = Path(f"/tmp/cmux_cpu_test_sample_{pid}.txt")
|
||||
sample_file.write_text(sample_output)
|
||||
print(f"\nFull sample saved to: {sample_file}")
|
||||
|
||||
|
|
@ -196,7 +196,7 @@ def main():
|
|||
lines = sample_output.split("\n")
|
||||
relevant_lines = [
|
||||
l for l in lines
|
||||
if "cmuxterm" in l and ("body" in l or "Animation" in l or "Timer" in l)
|
||||
if "cmux" in l and ("body" in l or "Animation" in l or "Timer" in l)
|
||||
][:10]
|
||||
for line in relevant_lines:
|
||||
print(f" {line.strip()[:100]}")
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
Automated test for ctrl+enter keybind using real keystrokes.
|
||||
|
||||
Requires:
|
||||
- cmuxterm running
|
||||
- cmux running
|
||||
- Accessibility permissions for System Events (osascript)
|
||||
- keybind = ctrl+enter=text:\\r (or \\n/\\x0d) configured in Ghostty config
|
||||
"""
|
||||
|
|
@ -115,14 +115,14 @@ def test_ctrl_enter_keybind(client: cmux) -> tuple[bool, str]:
|
|||
|
||||
def run_tests() -> int:
|
||||
print("=" * 60)
|
||||
print("cmuxterm Ctrl+Enter Keybind Test")
|
||||
print("cmux Ctrl+Enter Keybind Test")
|
||||
print("=" * 60)
|
||||
print()
|
||||
|
||||
socket_path = cmux.default_socket_path()
|
||||
if not os.path.exists(socket_path):
|
||||
print(f"SKIP: Socket not found at {socket_path}")
|
||||
print("Tip: start cmuxterm first (or set CMUX_TAG / CMUX_SOCKET_PATH).")
|
||||
print("Tip: start cmux first (or set CMUX_TAG / CMUX_SOCKET_PATH).")
|
||||
return 0
|
||||
|
||||
config_path = find_config_with_keybind()
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ Regression: GhosttyKit already injects zsh shell integration by setting ZDOTDIR
|
|||
to Ghostty's own integration directory (and optionally preserving a user-set
|
||||
ZDOTDIR in GHOSTTY_ZSH_ZDOTDIR).
|
||||
|
||||
cmuxterm also injects its own zsh integration by setting ZDOTDIR to
|
||||
Resources/shell-integration. If cmuxterm incorrectly treats Ghostty's injected
|
||||
cmux also injects its own zsh integration by setting ZDOTDIR to
|
||||
Resources/shell-integration. If cmux incorrectly treats Ghostty's injected
|
||||
ZDOTDIR as the "user" ZDOTDIR, zsh history will be isolated to the integration
|
||||
directory rather than the user's HOME/ZDOTDIR, breaking cross-terminal history
|
||||
and therefore zsh-autosuggestions.
|
||||
|
|
@ -63,7 +63,7 @@ def main() -> int:
|
|||
env.pop("GHOSTTY_SHELL_FEATURES", None)
|
||||
env.pop("GHOSTTY_BIN_DIR", None)
|
||||
|
||||
# Simulate the buggy situation: cmuxterm stores Ghostty's injected ZDOTDIR
|
||||
# Simulate the buggy situation: cmux stores Ghostty's injected ZDOTDIR
|
||||
# as the "original" ZDOTDIR, then sets ZDOTDIR to its own wrapper.
|
||||
env["CMUX_ORIGINAL_ZDOTDIR"] = str(ghostty_zsh_dir)
|
||||
env["ZDOTDIR"] = str(cmux_wrapper_dir)
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@
|
|||
Regression: if the user's .zshenv changes ZDOTDIR, then .zshrc should be sourced
|
||||
from the updated ZDOTDIR (matching vanilla zsh semantics).
|
||||
|
||||
Why this matters for cmuxterm:
|
||||
- cmuxterm sets ZDOTDIR to the app wrapper directory so zsh loads wrapper
|
||||
Why this matters for cmux:
|
||||
- cmux sets ZDOTDIR to the app wrapper directory so zsh loads wrapper
|
||||
startup files.
|
||||
- The wrapper .zshenv temporarily restores ZDOTDIR to the original directory
|
||||
while sourcing the user's real .zshenv.
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
Regression: zsh wrapper startup files must source user files with the *original*
|
||||
ZDOTDIR, not the wrapper directory.
|
||||
|
||||
The cmuxterm zsh integration sets ZDOTDIR to the app's wrapper directory so zsh
|
||||
The cmux zsh integration sets ZDOTDIR to the app's wrapper directory so zsh
|
||||
loads wrapper .zshenv/.zprofile/.zshrc. Those wrappers must temporarily restore
|
||||
ZDOTDIR while sourcing the user's real startup files so $ZDOTDIR semantics match
|
||||
normal zsh behavior.
|
||||
|
|
|
|||
|
|
@ -89,8 +89,8 @@ def _git(cwd: Path, *args: str) -> None:
|
|||
def _init_git_repo(repo: Path) -> None:
|
||||
repo.mkdir(parents=True, exist_ok=True)
|
||||
_git(repo, "init")
|
||||
_git(repo, "config", "user.email", "cmuxterm-test@example.com")
|
||||
_git(repo, "config", "user.name", "cmuxterm-test")
|
||||
_git(repo, "config", "user.email", "cmux-test@example.com")
|
||||
_git(repo, "config", "user.name", "cmux-test")
|
||||
(repo / "README.md").write_text("hello\n", encoding="utf-8")
|
||||
_git(repo, "add", "README.md")
|
||||
_git(repo, "commit", "-m", "init")
|
||||
|
|
@ -103,7 +103,7 @@ def _init_git_repo(repo: Path) -> None:
|
|||
|
||||
|
||||
def main() -> int:
|
||||
tag = os.environ.get("CMUX_TAG") or os.environ.get("CMUXTERM_TAG") or ""
|
||||
tag = os.environ.get("CMUX_TAG") or ""
|
||||
if not tag:
|
||||
print("Tip: set CMUX_TAG=<tag> when running this test to avoid socket conflicts.")
|
||||
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ def _find_free_allowed_port() -> int:
|
|||
|
||||
def _start_external_server(base: Path, port: int) -> subprocess.Popen:
|
||||
"""
|
||||
Start an http.server outside cmuxterm and ensure it is actually listening.
|
||||
Start an http.server outside cmux and ensure it is actually listening.
|
||||
Retries are handled by the caller by picking a different port.
|
||||
"""
|
||||
proc = subprocess.Popen(
|
||||
|
|
@ -201,7 +201,7 @@ def _wait_for_lsof_listen_gone(port: int, timeout: float = 8.0) -> None:
|
|||
|
||||
|
||||
def main() -> int:
|
||||
tag = os.environ.get("CMUX_TAG") or os.environ.get("CMUXTERM_TAG") or ""
|
||||
tag = os.environ.get("CMUX_TAG") or ""
|
||||
if not tag:
|
||||
print("Tip: set CMUX_TAG=<tag> when running this test to avoid socket conflicts.")
|
||||
|
||||
|
|
@ -215,7 +215,7 @@ def main() -> int:
|
|||
shutil.rmtree(base)
|
||||
base.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Start a listening server outside cmuxterm. A fresh tab should NOT show this port,
|
||||
# Start a listening server outside cmux. A fresh tab should NOT show this port,
|
||||
# since ports should be attributed to the shell session in the tab.
|
||||
port = None
|
||||
last_start_err: Exception | None = None
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Regression: cmuxterm relies on Ghostty's xterm-ghostty terminfo entry.
|
||||
Regression: cmux relies on Ghostty's xterm-ghostty terminfo entry.
|
||||
|
||||
In cmuxterm (embedded GhosttyKit), "bright" SGR 90-97 can render incorrectly
|
||||
In cmux (embedded GhosttyKit), "bright" SGR 90-97 can render incorrectly
|
||||
for some palettes. zsh-autosuggestions defaults to `fg=8`, which historically
|
||||
resolved to SGR 90 via terminfo `setaf`.
|
||||
|
||||
cmuxterm ships a terminfo overlay that forces bright colors to use indexed
|
||||
cmux ships a terminfo overlay that forces bright colors to use indexed
|
||||
256-color sequences (`38;5;<n>` / `48;5;<n>`) instead of SGR 90-97/100-107.
|
||||
This test ensures the overlay remains in place.
|
||||
"""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue