* Pre-launch app for browser UI test on headless CI runners
XCUIApplication.launch() blocks ~60s then fails on headless WarpBuild
runners because foreground activation requires a GUI login session.
Apply the same pre-launch strategy used for the display resolution test:
- CI shell launches the app with env vars before running xcodebuild
- Test detects pre-launched app via manifest, uses activate() instead of
launch() to avoid killing and relaunching the app
- Falls back to clicking the window for focus via accessibility framework
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Revert "Pre-launch app for browser UI test on headless CI runners"
This reverts commit a540e2fd99aaa1395b91a8d50caa797cdd7551b8.
* feat: cmux.json for custom commands
* tests: add cmux json tests
* fix: pr review feedback: validation, translations, input handling, and palette improvements
- Fix Danish ("Overfladedef inition") and Norwegian ("rotmapp") translation typos
- Add empty-string check for baseCwd fallback in command palette handlers
- Coalesce \r\n into single Return keypress in sendInput
- Redact command text from timeout log to prevent secret leakage
- Add decode-time validation: reject hybrid/empty commands, ambiguous layout
nodes, wrong split children count, and empty pane surfaces
- Namespace custom command IDs with "cmux.config.command." prefix
- Forward command description to palette subtitle when available
- Update tests for new validation rules and ID prefix
* fix: address PR review feedback — per-window config isolation, blank validation, ancestor walk,
palette sanitization
* fix: fallback to current dir cmux.json watching if no any cmux.json found in full acesor walk
* ci: trigger CI for fork PR
* Add directory trust for cmux.json command confirmation
The confirm dialog now shows the actual command text and has an "Always
trust commands from this folder" checkbox. When checked, future confirm
commands from that directory skip the dialog.
Trust is scoped to the git repo root if the cmux.json is inside a repo,
so trusting once covers all subdirectories. Non-git directories are
trusted by exact path. Global config is always trusted.
Trusted directories are persisted in ~/Library/Application Support/cmux/
trusted-directories.json.
* Add trusted directories section to Settings
Shows all trusted directories with per-directory revoke buttons and a
Clear All option. Placed in a "Custom Commands" section between
Automation and Browser in Settings.
* Replace trusted directories list with editable textarea
One path per line, with a Save button that activates on changes.
Users can add, remove, or edit paths directly.
* Auto-save trusted directories on edit, remove Save button
Matches the behavior of other textarea settings (browser host
whitelist, external URL patterns) which auto-save via @AppStorage.
* Sanitize command text in confirm dialog against BiDi attacks
Strip zero-width and BiDi override characters from the command preview
so the dialog shows exactly what will be executed.
---------
Co-authored-by: austinpower1258 <austinwang115@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
* Pre-launch app for browser UI test on headless CI runners
XCUIApplication.launch() blocks ~60s then fails on headless WarpBuild
runners because foreground activation requires a GUI login session.
Apply the same pre-launch strategy used for the display resolution test:
- CI shell launches the app with env vars before running xcodebuild
- Test detects pre-launched app via manifest, uses activate() instead of
launch() to avoid killing and relaunching the app
- Falls back to clicking the window for focus via accessibility framework
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Revert "Pre-launch app for browser UI test on headless CI runners"
This reverts commit a540e2fd99aaa1395b91a8d50caa797cdd7551b8.
* Fix browser portal leaking to other tabs on Bonsplit tab switch
When switching between Bonsplit tabs within a workspace, portal-hosted
WKWebViews from deselected browser panels could remain visible above the
newly selected tab. This happened because Bonsplit's keepAllAlive mode
hides non-selected tabs via SwiftUI .opacity(0), but the portal layer
renders at the AppKit window level and is not affected by SwiftUI
opacity changes.
Explicitly hide browser portals for deselected tabs in the pane during
tab selection, ensuring the portal visibility state is always in sync
regardless of SwiftUI re-render timing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: austinpower1258 <austinwang115@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
- Add hasLiveSurface guard in TabManager.inheritedTerminalConfigForNewWorkspace
to protect the cmuxInheritedSurfaceConfig call site missed in the initial fix
- Expand malloc_size comment to clarify it is a best-effort heuristic, not a
lifetime guarantee (per Copilot review)
ghostty_surface_quicklook_font returns an unretained CTFont pointer that
can become stale on Intel Macs, leading to EXC_BAD_ACCESS (SIGSEGV) when
creating a split. This is a follow-up to the same crash pattern fixed in
#1496.
Add malloc_size validation in cmuxCurrentSurfaceFontSizePoints to detect
freed heap allocations before interpreting the pointer as a CTFont. Also
add hasLiveSurface guards in inheritedTerminalConfig and
rememberTerminalConfigInheritanceSource to skip surfaces whose native
state is closing or closed. All callers already handle nil gracefully by
falling back to inherited config values.
* Add regressions for SSH image transfer followups
* Fix SSH image transfer followup regressions
---------
Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
Include the panel's custom title, terminal title, or directory name in
the close tab confirmation dialog's informative text so users know which
tab will be closed. Falls back to the generic message when no name is
available. The dialog title stays "Close tab?" for consistency with the
close-confirmation detection in AppDelegate.
Fixes#1603
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The ghostty_surface_quicklook_font function returns an unretained pointer to a font object. Using takeRetainedValue() transferred non-existent ownership to ARC, leading to an over-release and an eventual EXC_BAD_ACCESS (SIGSEGV) crash when creating new surfaces (like cmd+t or cmd+d) on certain systems such as Intel Macs.
Replaced takeRetainedValue() with takeUnretainedValue() to correctly manage memory.
Co-authored-by: LeonLeung <leonleung.tech@gmail.com>
* Fix stale Claude status in sidebar by adding missing hooks and OSC suppression
The Claude Code integration only used 3 hooks (SessionStart, Stop, Notification),
leaving gaps that caused stale sidebar status. Now uses 6 hooks:
- SessionEnd: clears status when Claude exits (covers Ctrl+C where Stop doesn't fire)
- UserPromptSubmit: clears "Needs input" and sets "Running" on new prompt
- PreToolUse (async): clears "Needs input" when Claude resumes after permission grant
Also:
- Suppress OSC 9/99 desktop notifications for workspaces with active Claude hook
sessions to prevent duplicates from the raw OSC path
- Store Claude process PID in status entries for stale-session detection
- Add 30-second sweep timer that checks agent PIDs and clears stale entries
(safety net for SIGKILL/crash where no hook fires)
- Update wrapper test expectations for the new hook set
Fixes https://github.com/manaflow-ai/cmux/issues/1301
* Don't show "Running" status on Claude launch, only when actually working
SessionStart now registers the PID for tracking and OSC suppression via
set_agent_pid without setting a visible status entry. "Running" only
appears when the user submits a prompt (UserPromptSubmit) or Claude
starts using tools (PreToolUse).
Added set_agent_pid / clear_agent_pid socket commands to decouple PID
tracking from visible status entries. OSC suppression checks agentPIDs
instead of statusEntries so it works during the initial idle period.
* Don't restore status entries across app restarts
Status entries are ephemeral runtime state tied to running processes
(e.g. claude_code "Running"). Restoring them after restart shows stale
status for processes that no longer exist.
* Address PR review comments and remove debug logging
- session-end: only clear status/PID/notifications when Stop didn't fire first
- PID sweep: check errno == ESRCH instead of treating all kill(pid,0) failures as dead
- Validate CMUX_CLAUDE_PID > 0
- Propagate tracked PID in pre-tool-use setClaudeStatus
- OSC suppression: use tabManagerFor(tabId:) for multi-window support
- clearAgentPID: resolve tab UUID before async dispatch
- restoreSessionSnapshot: also clear agentPIDs alongside statusEntries
- Fix AskUserQuestion surfaceId overwrite (wrong workspace notification)
- Fix notification text matching for "Claude Code needs your attention"
- AskUserQuestion: render option labels as bracketed inline text
- Remove artificial text truncation limits
- Remove temporary JSONL debug logging from all handlers
* Use resolveTabIdForSidebarMutation in clearAgentPID
Previously, Cmd+W on the last surface kept the workspace alive with a
replacement shell, unless the user toggled a hidden setting. This was
confusing—users expect Cmd+W to close the window when there's nothing
left.
Now Cmd+W (and the tab-strip X button) always close the workspace when
they close its last surface, and close the window when that was the last
workspace. Internal/programmatic closes (e.g. process exit, panel moves)
still spawn a replacement shell so the workspace stays alive.
Key changes:
- Track explicit user close gestures via markExplicitClose / onTabCloseRequest
- Remove the LastSurfaceCloseShortcutSettings toggle (now always-on)
- Use window.performClose for last-workspace window close
- Update tests to match the new behavior
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>