Commit graph

1872 commits

Author SHA1 Message Date
Lawrence Chen
84af32c56e
Add cmux omo command for oh-my-openagent integration (#2087)
* Add `cmux omo` command for OpenCode + oh-my-openagent integration

Same pattern as `cmux claude-teams`: creates a tmux shim so
oh-my-openagent's TmuxSessionManager spawns agents as native cmux
splits instead of tmux panes. Sets TMUX/TMUX_PANE env vars, prepends
shim to PATH, and execs into opencode.

Closes https://github.com/manaflow-ai/cmux/issues/2085

* Auto-install oh-my-opencode plugin when running cmux omo

Before launching opencode, cmux omo now:
- Checks if oh-my-opencode is registered in ~/.config/opencode/opencode.json
- If not, creates/updates the config with the plugin entry
- Checks if the npm package is installed in node_modules
- If not, runs bun add (or npm install) to install it
- Then proceeds with tmux shim setup and exec

* Use shadow config dir to avoid modifying user's opencode setup

Instead of writing directly to ~/.config/opencode/opencode.json,
cmux omo now creates a shadow config at ~/.cmuxterm/omo-config/ that
layers oh-my-opencode on top of the user's existing config. Symlinks
node_modules, package.json, bun.lock, and plugin config from the
original dir. Sets OPENCODE_CONFIG_DIR to the shadow directory.

Running plain `opencode` remains unaffected.

* Add Agent Integrations docs section with Claude Code Teams and oh-my-opencode pages

Adds sectioned sidebar navigation to the docs site. The new Agent
Integrations section contains separate pages for cmux claude-teams and
cmux omo, documenting usage, tmux shim mechanics, directory layout,
environment variables, and the shadow config approach. Both pages
include a nightly-only warning. Full English and Japanese translations,
nav item keys added to all 19 locales.

* Remove uppercase from sidebar section headers

* Add more spacing above and below sidebar section headers

* Enable tmux mode in oh-my-opencode config, improve docs

- cmux omo now writes tmux.enabled=true to the shadow oh-my-opencode.json
  config. Without this, oh-my-openagent's TmuxSessionManager won't spawn
  visual panes even though $TMUX is set (the config defaults to false).
- Nightly warnings now link to /nightly instead of generic text.
- Added "What you get" section to oh-my-opencode docs explaining the
  visual pane behavior (auto-layout, idle cleanup, queueing).
- Added tmux.enabled step to first-run and how-it-works sections.

* Add terminal-notifier shim to route oh-my-openagent notifications to cmux

oh-my-openagent sends macOS notifications via terminal-notifier
(args: -title <t> -message <m> [-activate <id>]). The shim in
~/.cmuxterm/omo-bin/terminal-notifier intercepts these calls and
routes them through cmux notify, so notifications appear in cmux's
sidebar panel instead of as raw macOS notifications.

* Add pane geometry to tmux-compat for oh-my-openagent grid planning

oh-my-openagent's TmuxSessionManager needs pane geometry (columns,
rows, position, window dimensions) to decide where to spawn agent
panes. Without this data, agents run headlessly.

Server side:
- pane.list v2 response now includes pixel_frame, cell_size, columns,
  rows per pane, plus container_frame at the top level
- Uses BonsplitController.layoutSnapshot() for pixel geometry and
  ghostty_surface_size() for terminal grid dimensions

CLI side:
- tmuxEnrichContextWithGeometry() computes character-cell positions
  from pixel frames and cell dimensions for tmux format variables
  (pane_width, pane_height, pane_left, pane_top, pane_active,
  window_width, window_height)
- list-panes now resolves pane targets (%uuid) via tmuxResolvePaneTarget
  instead of failing with "Workspace not found"
- display-message enriched with geometry for format strings like
  #{pane_width},#{window_width}
- tmux -V now returns "tmux 3.4" (needed by oh-my-openagent's
  tmux-path-resolver verification)

* Add socket tests for tmux-compat pane geometry

6 tests verifying the geometry enrichment works end-to-end:
- pane.list returns pixel_frame, columns, rows, cell_size, container_frame
- tmux -V returns version string
- list-panes -F renders geometry format variables as integers
- list-panes -t %<uuid> resolves pane targets
- display -p renders pane_width and window_width
- After split, two panes have different positions and halved widths

All 6 pass on macmini (cmux-macmini).

* Handle tmux -V in shim script directly (no socket needed)

oh-my-openagent's tmux-path-resolver runs tmux -V to verify the binary
works. The __tmux-compat handler requires a socket connection, which
may not be established at verification time. Handle -V in the bash
shim directly to avoid the socket dependency.

* Lower default tmux pane min widths for cmux omo

oh-my-openagent defaults: main_pane_min_width=120, agent_pane_min_width=40,
requiring 161+ columns. Most terminal windows are narrower, causing
decideSpawnActions to return canSpawn=false and defer agents forever.

cmux omo now sets: main_pane_min_width=60, agent_pane_min_width=30,
main_pane_size=50, requiring only 91 columns. Also moved tmux -V
handling into the bash shim to avoid needing a socket connection for
the version check.

* Resolve merge conflicts with main (main-vertical layout, focus param)

- Keep upstream main-vertical layout anchoring from #2119
- Keep upstream focus param (v2Bool) instead of no_focus
- Combine with our -d flag handling: -d sets focus=false
- Include customCommands nav item from main

* Implement select-layout equalize and resize-pane absolute width

When oh-my-openagent spawns agent panes, it calls select-layout
main-vertical after each split to redistribute panes evenly, then
resize-pane -x <columns> to set the main pane width. Both were
previously no-ops, causing cascading uneven splits.

Server side:
- Add workspace.equalize_splits v2 API that calls the existing
  TabManager.equalizeSplits (sets all dividers to 0.5)

CLI side:
- select-layout now calls workspace.equalize_splits before tracking
  main-vertical state
- resize-pane -x <columns> without directional flags now computes
  the pixel delta from current to desired width and resizes accordingly

* Fix equalize to use proportional divider positions

The previous equalize set all dividers to 0.5, which in a right-
recursive binary tree (from successive splits) gives 50/25/12.5/6.25%
instead of equal sizes.

New algorithm counts leaf panes on each side of each split and sets
the divider to N_left / (N_left + N_right). For 5 panes in a chain:
1/5, 1/4, 1/3, 1/2, giving each pane exactly 20%.

* Fix select-layout main-vertical to only equalize vertical splits

The proportional equalize was treating the top-level horizontal split
(main vs agent column) the same as vertical splits, setting the main
pane to 1/6 of the window with 5 agents.

For main-vertical layout, only equalize vertical splits (the agent
column), leaving the horizontal main/agent divider untouched. The
subsequent resize-pane -x handles the main pane width.

workspace.equalize_splits now accepts an optional orientation filter
("vertical" or "horizontal") to scope which splits get equalized.

* Re-equalize agent column after kill-pane

* Address PR review comments

- Fix cmux omo --help: remove omo from the help-bypass guard so
  --help shows usage text instead of trying to launch opencode
- Don't overwrite unreadable opencode.json: fail with an error
  instead of silently resetting to empty config
- Drain installer pipes concurrently before waitUntilExit to
  prevent deadlock from full pipe buffers during bun/npm install

---------

Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
2026-03-26 16:07:59 -07:00
Lawrence Chen
ee3460b8d7
docs: add Copilot CLI hooks section to i18n notifications page (#2217)
Follow-up to #1875 which added Copilot CLI hook instructions to
docs/notifications.md but not the live docs site. Adds the section
to page.tsx with translation keys for all 19 locales.

Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
2026-03-26 16:03:07 -07:00
Austin Wang
32124d9256
Revert "Merge pull request #1915 from elvistranhere/fix/split-crash-intel-1870" (#2221)
This reverts commit c5b306655d, reversing
changes made to 100612d96e.
2026-03-26 16:01:53 -07:00
Lawrence Chen
bc9e45c897
Fix workspace creation snapshot crash (#2176)
* Add workspace creation config regression test

* Sanitize workspace creation config snapshot

* Remove duplicate WorkspaceCreationConfigSanitizationTests class

Main already has this test class; the branch's version was a duplicate
that would cause a compilation error after merge.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
Co-authored-by: austinpower1258 <austinwang115@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-26 15:53:21 -07:00
Matt Van Horn
819ceb8ebb
feat(sidebar): make listening ports clickable to open in browser (#1844)
* feat(sidebar): make listening ports clickable to open in browser

Wrap each sidebar port in a Button that opens http://localhost:{port}
in the cmux built-in browser (or system browser as fallback), matching
the existing PR link click behavior.

Fixes #1602

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address bot review feedback on port clickability

- Localize port label text with String(localized:) instead of bare literal
- Add sidebar.port.label and sidebar.port.openTooltip keys to
  Localizable.xcstrings with English and Japanese translations
- Respect openSidebarPullRequestLinksInCmuxBrowser user preference in
  openPortLink, matching the openPullRequestLink pattern exactly

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-26 15:49:38 -07:00
Harley Adams
9d8e6a8d05
docs: add Copilot CLI hook setup to notifications guide (#1875)
* docs: add Copilot CLI hook setup to notifications guide

Add GitHub Copilot CLI section with hooks for notifications and
sidebar status pills (userPromptSubmitted, agentStop, errorOccurred,
sessionEnd). Includes link to official GitHub hooks documentation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* docs: address PR feedback for Copilot CLI hooks

- Use if/else instead of &&/|| to avoid false-positive osascript fallback
- Fix jq path: .error.message → .errorMessage for Copilot CLI schema
- Remove --icon/--color flags (let cmux handle styling)
- Add clear-notifications side-effect note
- Add concrete repo-level config example
- Document set-status/clear-status in CLI Commands section

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Harley Adams <harley.adams@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-26 15:49:36 -07:00
Mikhail Andreev
cfe6cf89d8
fix: keyboard shortcuts not working with Russian layout (#2202)
* fix: keyboard shortcuts not working with Russian and other non-Latin layouts

When a non-Latin input source (Russian, etc.) is active, event characters
are non-ASCII. The ANSI keyCode fallback was blocked when the layout-based
translation resolved a character, leaving no safety net. Now the keyCode
fallback is always available for non-Latin layouts, matching the physical
key position — similar to Ghostty's `physical:` keybinding behavior.

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

* test: add unit tests for Russian keyboard layout shortcut matching

Two tests for Cmd+T with non-Latin (Russian) keyboard layout:
1. Layout provider returns "t" (normal ASCII fallback) — verifies
   the layout-based matching path works with Cyrillic event chars.
2. Layout provider returns nil (translation failure) — verifies the
   ANSI keyCode fallback catches the shortcut by physical key position.

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

* fix: scope ANSI keyCode fallback to non-ASCII events, add Russian to comments

Address review feedback:
- Split !hasUsableEventChars into two precise conditions:
  (hasEventChars && !eventCharsAreASCII) for non-Latin layouts, and
  (!hasEventChars && layoutCharacter empty) for synthetic/empty-char events.
  This prevents unintended keyCode fallback on Dvorak/Colemak with empty
  synthetic events.
- Add "Russian" to the non-Latin layout list in the guard comment at line 10626.

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

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 15:25:36 -07:00
Usman Ehtesham Gul
66b0260442
Add --name flag to new-workspace CLI command (#2160)
Allow naming a workspace at creation time instead of requiring a
separate rename-workspace call afterward.

Threads a title parameter through:
- CLI: --name <title> flag parsed and sent as "title" in v2 params
- V2 handler: extracts title, passes to TabManager.addWorkspace()
- TabManager: uses provided title instead of auto-generated
  "Terminal N" and calls setCustomTitle() to persist it
- V1 handler: accepts optional name argument

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-26 15:18:20 -07:00
Achieve
0978732c93
fix: route PWD action to correct TabManager per tabId (#2147)
Previously, GHOSTTY_ACTION_PWD used AppDelegate.shared?.tabManager to
update the current working directory, which only returns the key window's
TabManager. In multi-window scenarios, cwd updates from non-key windows
were written to the wrong TabManager, causing panelDirectories to be
empty at save time and falling back to $HOME on restore.

Fix by using tabManagerFor(tabId:) to precisely route the update to
whichever window owns the tab, ensuring all windows persist their
working directories correctly.

Fixes #2125
2026-03-26 15:16:12 -07:00
Austin Wang
ccaebbd112
Fix workspace shortcut docs (#2211) 2026-03-26 15:13:50 -07:00
HyunJick Lee
46f560fd3e
chore: remove unused koreanRanges constant (#2158)
The Korean CJK font mapping added in PR #1017 was removed in PR #1700,
but the koreanRanges static property was left behind as dead code.

Related: #1700, #1693
2026-03-26 15:12:35 -07:00
Achieve
6a41c9f42f
fix(quit): enforce Warn Before Quit when Cmd+Q arrives via app switcher (#2186)
applicationShouldTerminate was returning .terminateNow unconditionally,
bypassing QuitWarningSettings. Now it shows the same confirmation alert
used by handleQuitShortcutWarning, with an isQuitWarningConfirmed flag
to prevent a double dialog when the Cmd+Q shortcut path already confirmed.

Fixes #2139

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 15:12:32 -07:00
Lawrence Chen
46589f531c
Fix SEO indexing: hreflang, canonicals, sitemap, trailing slash (#2193)
* Fix SEO indexing: add hreflang, canonicals, sitemap per-locale entries

Google Search Console showed 380 not-indexed vs 86 indexed pages.
Root causes: missing hreflang tags on rendered pages (only in sitemap),
no canonical on homepage, inconsistent canonicals wiping parent hreflang,
sitemap only listing English URLs, trailing slash duplicates, and
_next/static chunks being crawled as pages.

Changes:
- Add buildAlternates() utility for consistent canonical + hreflang
- Add hreflang tags to all pages via alternates.languages in metadata
- Add self-referencing canonical URLs to every page (homepage had none)
- Expand sitemap to emit separate entries for each locale
- Add missing /docs/custom-commands to sitemap
- Remove skipTrailingSlashRedirect to normalize trailing slashes
- Block /_next/ in robots.txt to stop chunk crawling

* Add per-page alternates to docs sub-pages and blog index

Docs sub-pages and blog index only returned title/description in
generateMetadata, so they inherited the parent layout's alternates
(pointing to /docs or /blog). Now each page sets its own
buildAlternates() with the correct path so canonical and hreflang
point to the actual page URL.

* Derive openGraph.url from buildAlternates to avoid drift

* Redirect non-English legal pages to English, remove from sitemap

Legal pages (privacy policy, TOS, EULA) are untranslated English content.
Serving them under every locale creates 54 duplicate URLs. Now:
- Middleware 301-redirects /ja/privacy-policy etc. to /privacy-policy
- Sitemap only includes English URLs for legal pages (no locale variants)
- Legal page metadata uses static English-only canonical

* Fix legal page redirect to only match /<locale>/<page> paths

endsWith matched too broadly (e.g. /docs/eula). Now only redirects
when the path after the first segment is an exact legal page match.

* Skip next-intl for legal pages to prevent locale redirect loop

Without this, a Japanese user hitting /privacy-policy could be
redirected by next-intl to /ja/privacy-policy, which our middleware
redirects back to /privacy-policy, creating a loop.

* Rewrite legal pages to /en/ instead of NextResponse.next()

Pages live under app/[locale]/, so skipping next-intl entirely
would break route resolution. Rewrite to /en/privacy-policy etc.
so Next.js can resolve the [locale] segment correctly.

---------

Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
2026-03-26 15:10:39 -07:00
Lawrence Chen
f507fd8141
Fix claude-teams pane anchoring with main-vertical layout (#2119)
* Fix claude-teams pane anchoring: main-vertical layout + focus

Claude's agent teams sends `split-window -h` for each teammate then
`select-layout main-vertical` to stack them vertically. Three fixes:

1. Implement select-layout main-vertical: track layout state in the
   tmux compat store so subsequent horizontal splits of the leader
   pane get redirected to vertical splits of the right-side column.

2. Pass focus:false to surface.split from the split-window handler
   so internal bonsplit focus stays on the leader pane.

3. Fix tmuxCompatStoreURL to respect $HOME env var (was using
   NSString.expandingTildeInPath which ignores $HOME).

Closes https://github.com/manaflow-ai/cmux/issues/2118

* Fix claude-teams split routing: auto-seed main-vertical on first right split

The split-window routing was broken in two ways:

1. After the first teammate split (right), the main-vertical state wasn't
   being created, so all subsequent splits also went right instead of
   stacking down in the right column.

2. The old code only redirected splits when main-vertical was already active
   AND the target was the leader surface. Claude's teams protocol targets
   arbitrary panes from list-panes, not necessarily the leader.

Now the first teammate split goes right (creating the column), auto-seeds
the main-vertical state, and all subsequent splits stack downward. The
caller's surface (CMUX_SURFACE_ID) is used as the anchor regardless of
which pane Claude targets.

Also adds caller surface preference in pane target resolution so the
caller's exact surface is used when the target pane matches, preventing
stale selected-surface references after tab switches.

---------

Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
2026-03-26 14:36:48 -07:00
Austin Wang
fe0443fa2b
Fix Cmd+N nightly crash: avoid local Workspace refs in ARC hotpath (#2204)
* test: reproduce Cmd+N snapshot workspace lifetime race

* fix: retain snapshot workspaces through Cmd+N creation

* fix: repair workspace lifetime regression test

* fix: extract workspace config through self to avoid Xcode 16.x ARC crash

The snapshot approach (c1998e34) navigated workspace → panel → surface
through local variables. Xcode 16.4's -O ARC optimizer aggressively
elides retains on these locals through inlined call chains, causing
use-after-free on every Cmd+N in CI-built nightlies.

Fix: extract preferredWorkingDirectory and inheritedTerminalFontPoints
through self (always retained) BEFORE capturing locals. The snapshot
is now purely value-typed with no Workspace references held in locals.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-26 14:28:32 -07:00
Austin Wang
8a378152e8
Fix Cmd+N crash: retain snapshot workspaces through creation (#2183)
* test: reproduce Cmd+N snapshot workspace lifetime race

* fix: retain snapshot workspaces through Cmd+N creation

* fix: repair workspace lifetime regression test
2026-03-25 19:43:22 -07:00
Austin Wang
61e6a0e2b9
Fix Release-only Cmd+N workspace snapshot UAF (#2181)
* test: reproduce Cmd+N snapshot workspace lifetime race

* fix: retain snapshot workspaces through Cmd+N creation

* fix: repair workspace lifetime regression test
2026-03-25 19:12:24 -07:00
Lawrence Chen
18531fd78e
Merge pull request #2174 from manaflow-ai/task-pr-2089-followup-comments
Fix command palette follow-up review comments
2026-03-25 18:24:37 -07:00
Austin Wang
b93be12b08
Fix Cmd+N crash from workspace creation config snapshots (#2178)
* Add workspace config sanitization regression test

* Sanitize workspace creation config snapshots

---------

Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
2026-03-25 18:10:25 -07:00
Lawrence Chen
0a1d8c2289
Skip SSH cleanup after moving the last remote surface (#2123)
* Add regression test for detached remote cleanup

* Skip SSH cleanup after remote surface transfer

* Add SSH lifecycle regression coverage

* Add SSH detach cleanup transfer regressions

* Transfer SSH cleanup ownership with detached remote terminals

* Document intentional SSH workspace focus

---------

Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
2026-03-25 17:51:56 -07:00
Lawrence Chen
95914243c3
Fix UI regression test self capture 2026-03-25 17:51:15 -07:00
Lawrence Chen
9969c5e6a1
Fix NSTextView focus-stealer fallback 2026-03-25 17:40:46 -07:00
Lawrence Chen
9295a7f676
Add command palette follow-up regressions 2026-03-25 17:40:38 -07:00
Lawrence Chen
58dc932248
Set CMUX_SOCKET_MODE=allowAll in reload.sh for dev builds (#2165)
Dev builds should use the most permissionless socket mode so coding
agents and external tools can connect without any restrictions.
Changes automation → allowAll in the LSEnvironment plist entries and
the launch-time env vars.

Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
2026-03-25 17:31:27 -07:00
Lawrence Chen
8a3ab6b3f0
Fix command palette focus after terminal find (#2089)
* test: cover command palette focus guard

* fix: block terminal find from stealing palette focus

* test: cover text view focus-stealer fallback

* Add regression for hidden DevTools sync republish loop

* Avoid redundant DevTools visibility publishes

* test: cover browser find focus after workspace round-trip

* fix: restore browser find focus after workspace round-trip

* fix: keep browser find caret on workspace return

* Add workspace round-trip split find regressions

* Keep inactive find overlays from stealing focus

---------

Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
2026-03-25 17:27:54 -07:00
Lawrence Chen
b42f64fbe3
Fix update attempt refreshing pill without actually updating (#2168)
* Fix update attempt refreshing pill without actually updating

The attemptUpdate() subscriber watched for .updateAvailable state to
auto-confirm, but showUpdateFound used setStateAfterMinimumCheckDelay
which delays the transition by up to 2 seconds. During that window,
dismissUpdateInstallation (from a background probe race) could cancel
the pending transition, reverting state to idle without ever confirming.
The subscriber then tore down on the transient idle, silently abandoning
the update.

Fix: move auto-confirm to the Sparkle driver level via an
autoInstallOnNextUpdate flag. When set, showUpdateFound immediately
calls reply(.install) bypassing the delay entirely. The subscriber
is kept as a fallback but no longer tears down on transient idle
while the flag is active.

Closes https://github.com/manaflow-ai/cmux/issues/2166

* Revert "Fix update attempt refreshing pill without actually updating"

This reverts commit 1cd842dd924bf114b096f222851c47d2e36ad4d9.

* Fix update attempt refreshing pill without actually updating

The attemptUpdate() subscriber tore down monitoring whenever it saw
.idle after observing progress. During check startup (retry loop,
background probe race), state can transiently return to .idle before
Sparkle's interactive check begins. The subscriber interpreted this
as "check completed" and stopped monitoring, so the auto-confirm
for .updateAvailable never fired.

Fix: add !state.isIdle to the teardown guard so monitoring only
stops on terminal failures (.notFound, .error), not transient idle.

Closes https://github.com/manaflow-ai/cmux/issues/2166

---------

Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
2026-03-25 16:55:29 -07:00
Austin Wang
cbf08459d6
Fix Cmd+N workspace creation snapshot crashes (#2173)
* Add regression tests for Cmd+N workspace snapshot mutations

* Fix Cmd+N workspace creation snapshot races
2026-03-25 16:52:04 -07:00
Austin Wang
9f2adce830
fix: guard inherited terminal config against stale surfaces (#2101)
* test: add stale inherited surface regression

* fix: guard inherited terminal config against stale surfaces

* fix: address stale surface review feedback
2026-03-25 16:49:54 -07:00
Austin Wang
99ca3c9b9a
Fix sidebar update pill cached popover flow (#2142)
* test: cover cached update pill first-click flow

* fix: use cached sidebar update popover
2026-03-25 04:21:03 -07:00
Austin Wang
049d296267
Fix browser pane restore after reopen (#2141) 2026-03-25 04:11:43 -07:00
Austin Wang
11a841e020
Merge pull request #2133 from manaflow-ai/issue-2131-cmd-n-crash-regression
Fix Cmd+N crash from stale workspace creation snapshots
2026-03-25 02:40:57 -07:00
austinpower1258
17e8bb172d Fix Cmd+N crash from stale workspace creation snapshots 2026-03-25 02:38:20 -07:00
austinpower1258
ad2c65e0ac Add regression test for Cmd+N snapshot close race 2026-03-25 02:38:10 -07:00
Austin Wang
71a64a1234
Fix titlebar double-click zoom handling (#2130) 2026-03-25 02:15:15 -07:00
Austin Wang
0ea16b12c2
Fix window position restore on relaunch (#2129)
* test: cover accessible window frame restore

* fix: preserve accessible window frame on relaunch
2026-03-25 02:05:13 -07:00
Austin Wang
da70f3fa47
Add regression coverage for Cmd+N workspace creation crash (#2127)
* Add Cmd+N workspace snapshot regression coverage (#2017)

* Add dev flag to stress Cmd+N workspace creation
2026-03-25 01:50:57 -07:00
Lawrence Chen
0d8597caf9
New window inherits size from current window (#2124)
* New window inherits size from current window

When creating a new window via Cmd+Shift+N, use the key window's
frame dimensions instead of the hardcoded 460x360 default. The new
window cascades from the existing window's position so it doesn't
stack directly on top.

* Use Ghostty's cascade algorithm for new window positioning

Match upstream Ghostty's window cascade logic: maintain a
lastCascadePoint that tracks where the next window should appear.
First window seeds the point from its own top-left corner, subsequent
windows advance the cascade point via NSWindow.cascadeTopLeft(from:).
On window close, reset the cascade point to the closing window's
position so the next window appears nearby.

New windows still inherit the key window's size so Cmd+Shift+N
creates a window matching the previous one's dimensions.

* Fix frame-to-contentRect conversion and use preferred window resolver

Convert existingFrame to a content rect via
NSWindow.contentRect(forFrameRect:styleMask:) so the new window
matches the source window's actual size instead of growing by
titlebar insets on each Cmd+Shift+N.

Use preferredMainWindowContextForWorkspaceCreation to resolve the
source window, consistent with showOpenFolderPanel and other
callers.

---------

Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
2026-03-25 01:26:06 -07:00
Austin Wang
ffb660dee8
Fix first click on detected update pill (#2117) 2026-03-25 00:51:34 -07:00
Lawrence Chen
57237d9faa
Sanitize command before execution, not just display (#2122)
The confirm dialog showed a sanitized command (BiDi/zero-width stripped)
but executed the raw string, creating a display/consent mismatch.
Now the command is sanitized once and the same string is used for both
display and execution.

Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
2026-03-25 00:28:30 -07:00
Achieve
5b82041160
Handle Cmd+O in handleCustomShortcut to prevent Documents folder open (#2034)
* Handle Cmd+O in handleCustomShortcut to prevent Documents folder open

Cmd+O for "Open Folder" was only handled in SwiftUI menu, which can
fail due to focus bugs when terminal is focused. This caused AppKit's
default NSDocumentController to open the Documents folder instead.
Now Cmd+O is intercepted in handleCustomShortcut like other shortcuts.

Fixes #2010

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

* Fix fallback directory loss and deduplicate Open Folder logic

Address review feedback:
1. Pass selected directory URL to fallback window creation so the
   user's folder choice is not silently discarded
2. Replace inline NSOpenPanel code in cmuxApp.swift menu action
   with a call to AppDelegate.showOpenFolderPanel() to avoid
   future divergence between the two code paths

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

* Set NSOpenPanel directoryURL to current terminal working directory

Address review feedback: set panel.directoryURL to the focused
terminal's working directory so Open Folder starts in a contextually
relevant location instead of AppKit's default.

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

* Use shared main-window resolver and openWorkspaceForExternalDirectory in showOpenFolderPanel

Address review feedback: use preferredMainWindowContextForWorkspaceCreation
for directory seeding (works when auxiliary windows are key) and
openWorkspaceForExternalDirectory for workspace creation (ensures
shouldBringToFront and consistent fallback behavior).

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
2026-03-25 00:26:30 -07:00
jorge g
68ff39c444
Fix Ghostty resize_split keybind support (#1899)
* test: add resize_split regression coverage

* fix: implement Ghostty resize_split behavior

* test: cover more resize_split cases

* test: deduplicate split snapshot helper

* Resolve merge conflict: keep both splitNodes and waitForCondition helpers

---------

Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
2026-03-25 00:26:27 -07:00
Austin Wang
3952c2588b
Fix workspace creation crash after restore (#1985)
Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
2026-03-25 00:04:03 -07:00
Lawrence Chen
a7e5050552
Fix SSH control master cleanup on remote teardown (#2104)
* test: add SSH control master cleanup regressions

* fix: close SSH control master on remote teardown

* test: keep SSH workspace after child exit

* fix: keep SSH workspace after child exit

* fix: keep connecting SSH workspaces after child exit

* test: add SSH child-exit demotion regression

* fix: keep SSH workspace after connected shell exit

* fix: address SSH cleanup review feedback

* test: cover SSH cleanup without explicit controlpath

* fix: clean up SSH control masters without explicit controlpath

* test: cover remote detach cleanup edge cases

* fix: preserve SSH sessions during remote detach

---------

Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
2026-03-25 00:01:39 -07:00
Ariel Tobiana
7cbd07e8cb
Add set-color/clear-color workspace actions for tab color via CLI (#1873)
* Add set-color/clear-color workspace actions for tab color via CLI

Expose the existing tab color functionality through the workspace-action
CLI command, enabling programmatic tab color setting without the GUI
context menu.

Supports both named colors (Red, Blue, Amber, etc.) and hex values
(#RRGGBB). Named colors resolve against the built-in palette via
case-insensitive matching.

Usage:
  cmux workspace-action --action set-color --color blue
  cmux workspace-action --action set-color --color "#C0392B"
  cmux workspace-action set-color Amber
  cmux workspace-action clear-color

* Return explicit null color in clear_color JSON response

Restore "color": null in the clear_color response payload so JSON
consumers can distinguish "color was cleared" from "no color field".

---------

Co-authored-by: Ariel Tobiana <arieltobiana@gmail.com>
2026-03-24 23:58:54 -07:00
I LUK KIM
533699f98c
feat: add arrow keys, shift+tab, home/end/delete/pageup/pagedown to sendNamedKey (#1920)
The socket API's send-key command only supported enter, escape, tab,
backspace, and ctrl+letter. Arrow keys and shift+tab had to be sent
as raw escape sequences via send_text, but 0x1B is filtered out by
socketTextChunks and converted to a standalone Escape key event,
breaking multi-byte sequences like \x1b[A (ArrowUp).

This commit extends sendNamedKey to handle:
- Arrow keys: up/down/left/right (with arrow_up/arrowup aliases)
- shift+tab / shift-tab / backtab
- home, end
- delete / del / forward_delete
- pageup / page_up
- pagedown / page_down

These map directly to the existing sendKeyEvent infrastructure using
the appropriate kVK_* keycodes and modifier flags.

Co-authored-by: I Luk Kim <yirugi@gmail.com>
2026-03-24 23:58:51 -07:00
Lawrence Chen
71828fe86e
Reduce shell integration prompt latency (#2109)
* Reduce shell integration prompt latency

Three changes to cut ~10-15ms from every precmd/preexec cycle:

1. Use zsh/net/unix (zsocket) for socket sends when available. Eliminates
   fork+exec of ncat/socat/nc for every telemetry send (~3ms per send,
   3-4 sends per prompt cycle). Falls back to external tools if the
   module is unavailable.

2. Replace _cmux_kill_process_tree (synchronous /bin/ps -ax | awk) with
   direct kill in _cmux_stop_pr_poll_loop. The tree-kill enumerated all
   system processes on every command (~5-13ms). Orphaned children (gh,
   sleep) finish on their own within seconds.

3. Minor savings: guard _cmux_patch_ghostty_semantic_redraw after first
   success, make _cmux_clear_pr_for_panel async, cache bash send tool.

* Address review: process-group kill, fix clear_pr race, reorder bash init

1. Use kill -KILL -- -$PID (process-group kill) instead of plain kill.
   Background jobs are process-group leaders, so this kills all
   descendants (gh, sleep) without /bin/ps overhead.

2. Keep bash _cmux_clear_pr_for_panel synchronous to prevent race
   with the next report_pr from the poll loop. Zsh version uses
   _cmux_send_bg which is synchronous when zsocket is available.

3. Move _cmux_detect_send_tool after _cmux_fix_path in bash so the
   cached tool lookup runs with the final PATH.

---------

Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
2026-03-24 23:08:20 -07:00
Lawrence Chen
23253e6ddf
Validate workspace color during cmux.json decode (#2112)
Reject invalid color values at parse time with a clear error message
instead of silently ignoring them. Colors are normalized to #RRGGBB
via WorkspaceTabColorSettings.normalizedHex during decode.

Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
2026-03-24 22:58:27 -07:00
Austin Wang
321f8c14c8
fix(browser): keep IME Enter on composition path (#2108)
* test: cover browser IME Enter composition routing

* fix(browser): keep IME Enter on composition path
2026-03-24 22:56:55 -07:00
Austin Wang
983ef2537d
Skip sidebar PR lookup on main/master (#2110) 2026-03-24 22:56:40 -07:00
Pratik Pakhale
b9c656b90c
feat: cmux.json for custom commands (#2011)
* 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>
2026-03-24 22:28:46 -07:00