Skip to content

Add Dock sidebar TUI controls - #3217

Merged
lawrencecchen merged 66 commits into
mainfrom
feat-control-deck
Apr 29, 2026
Merged

lawrencecchen merged 66 commits into
mainfrom
feat-control-deck

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented Apr 28, 2026 •

Copy link
Copy Markdown
Contributor

Summary:

  • Adds Dock as the right-sidebar TUI control surface, with the built-in cmux feed TUI as the first control.
  • Adds project and global Dock config support via .cmux/dock.json and ~/.config/cmux/dock.json.
  • Adds docs for sharing, trust, reordering, and removing built-in controls.

Verification:

  • ./scripts/reload.sh --tag dock

Summary by cubic

Adds a new Dock in the right sidebar for persistent TUI controls and a keyboard-first Feed TUI (cmux feed tui) built on @opentui/core. Feed and Dock are separate modes; Dock controls run in login‑shell PTYs, persist across mode changes, forward shortcuts, and never steal focus.

  • New Features

    • Configurable Dock via .cmux/dock.json or ~/.config/cmux/dock.json (trust flow; per-control cwd, env, height). Built‑in Feed uses bundled cmux. Docs: docs/dock.md, docs/feed.md.
    • cmux feed tui [--opentui|--legacy] and cmux feed clear. OpenTUI Feed with keyboard nav and q/Ctrl‑C to quit.
    • Split right‑sidebar modes and shortcuts: keep Feed, add Dock (switchRightSidebarToDock). Labels, schema, localizations, docs, and tests updated.
  • Bug Fixes

    • Start Dock controls only after the terminal is ready. Run each in a non‑interactive login shell; on exit/interrupt, return to an interactive login shell.
    • Fixed Feed TUI socket race; payloads now include request_id, options, default_mode, with strict text limits.
    • Hide Dock terminals when switching modes (PTYs stay alive). Added debug route to focus/reveal Dock. E2E drives the Dock Feed TUI via an in‑app bridge/socket; CI installs Bun for tests.
    • Route right‑sidebar shortcuts via window key equivalents before the context guard so app shortcuts stay active and the TUI stays responsive.
    • Portal stats now include visible invalid‑anchor entries.
    • Fix Dock workspace retargeting so controls track the active workspace when switching tabs, and keep controls stable during workspace resolution.

Written for commit 1ff5e2f. Summary will update on new commits. Review in cubic

Summary by CodeRabbit

  • New Features

    • Interactive Dock panel in the right sidebar showing configurable controls from project/global config with a built-in fallback.
    • New cmux feed tui terminal TUI: keyboard-first navigation, accept/deny/replan/refresh actions.
  • UI/UX

    • Right-sidebar "Feed" mode rebranded to "Dock"; improved focus and keyboard behavior for docked terminals and controls.
    • Project Dock configs require a one-time trust approval.
  • Documentation

    • Added Dock guide and updated Feed docs for the TUI.
  • Tests

    • Updated UI tests for Dock mode and Feed TUI flows.
  • Localization

    • Sidebar labels updated and new Dock-related translations added.

@vercel

vercel Bot commented Apr 28, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment Apr 29, 2026 9:05am
cmux-staging Building Building Preview, Comment Apr 29, 2026 9:05am

@coderabbitai

coderabbitai Bot commented Apr 28, 2026 •

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a right‑sidebar "Dock" UI and runtime with config discovery (project → global → builtin) and trust gating, rebrands the sidebar from Feed→Dock, separates dock terminal focus routing, adds cmux feed tui TUI CLI, enriches feed socket payloads, updates tests/docs/localizations, and registers a new Dock Swift source.

Changes

Cohort / File(s) Summary
Dock UI & runtime
Sources/DockPanelView.swift
New SwiftUI/AppKit dock panel and runtime: strict config decoding, project/global/builtin discovery, trust fingerprinting & prompt, dedupe/ids, error/empty/trust states, toolbar (open/reload), layout/height distribution, per-control TerminalPanel creation with dock-focused surfaces and keyboard-focus bridge.
CLI: feed tui
CLI/cmux.swift
Adds cmux feed tui TTY-only keyboard-driven feed TUI: parses feed.list items (pending/resolvable logic, options, timestamps/title/detail extraction), renders scrollable card list, keyboard navigation, refresh/quit, and sends feed.permission.reply, feed.exit_plan.reply (with feedback path), and feed.question.reply. Help text updated.
Terminal surface & panel
Sources/GhosttyTerminalView.swift, Sources/Panels/TerminalPanel.swift
Introduces TerminalSurfaceFocusPlacement and focusPlacement on surfaces; registry helpers surface(id:) and isRightSidebarDockSurface(id:); TerminalPanel initializer forwards focusPlacement and additional environment to surfaces.
Focus coordination & AppDelegate
Sources/AppDelegate.swift, Sources/MainWindowFocusController.swift
Excludes right‑sidebar dock surfaces from certain terminal-keyboard-focus repair paths; adds registerDockHost(_:); treats dock host as a .feed focus endpoint; updates focus ownership/gating so dock surfaces count as owning focus but do not create terminal-focus repair requests.
Feed encoding & socket fields
Sources/Feed/FeedCoordinator.swift
Enriches socket JSON per-item: permission/exit-plan/question details (including plan_summary), tool execution/result fields, message text, stop reasons, todos array, and handles sessionStart/sessionEnd as no-additional-fields.
Right-sidebar & shortcuts
Sources/RightSidebarPanelView.swift, Sources/KeyboardShortcutSettings.swift, cmuxUITests/FeedSidebarUITests.swift
Rebrands UI label/icon and shortcut text from Feed→Dock; right-sidebar now uses DockPanelView(rootDirectory:); UI tests updated to Dock flow and socket ping/pong readiness handshake; deterministic locale/test launch envs added.
Localization & Xcode project
Resources/Localizable.xcstrings, GhosttyTabs.xcodeproj/project.pbxproj
Replaces/extends localization keys (Feed → Dock) in EN/JA/KO; registers DockPanelView.swift in Xcode project sources/build phases.
Docs
docs/dock.md, docs/feed.md
Adds docs/dock.md describing dock behavior, config precedence, trust model, built‑in feed control, keyboard-first usage and example .cmux/dock.json; updates docs/feed.md to note Feed accessible via cmux feed tui.
Portal debug metrics
Sources/TerminalWindowPortal.swift
Adds debug metric detecting visible portals with invalid/mismatched anchors; included in debug stats and integrity gate.
Tests/Minor
cmuxUITests/FeedSidebarUITests.swift
Refactors startup helper, replaces socket-file polling with ping/PONG handshake, modifies UI interactions to match Dock flow and verifies portal visibility across view switches.

Sequence Diagram

sequenceDiagram
    participant User as User
    participant RightSidebar as RightSidebar
    participant DockPanel as DockPanel
    participant ConfigLoader as ConfigLoader
    participant FileSystem as FileSystem
    participant TerminalPanel as TerminalPanel
    participant FocusCoordinator as FocusCoordinator

    User->>RightSidebar: Switch to Dock mode
    RightSidebar->>DockPanel: init(rootDirectory)
    DockPanel->>ConfigLoader: loadConfig()
    ConfigLoader->>FileSystem: check project .cmux/dock.json
    alt project config found
        FileSystem-->>ConfigLoader: return project config
    else
        ConfigLoader->>FileSystem: check ~/.config/cmux/dock.json
        alt global config found
            FileSystem-->>ConfigLoader: return global config
        else
            ConfigLoader-->>DockPanel: use built-in control
        end
    end
    ConfigLoader->>DockPanel: parsed controls + fingerprint
    alt untrusted project config
        DockPanel->>User: show trust prompt
        User-->>DockPanel: accept/reject
    end
    DockPanel->>TerminalPanel: create panels (focusPlacement=rightSidebarDock)
    TerminalPanel->>FocusCoordinator: register & request focus (dock-aware)
    User->>DockPanel: keyboard action (navigate/restart/open/reload)
    DockPanel->>TerminalPanel: execute control / route focus
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Poem

🐰 I hopped to the Dock where tiny terminals play,
I load trusted configs and pin tools for the day.
Keys hop and clack, panels wake, focus finds its spot,
Feed as a TUI — accept, deny, or plot. 🥕✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 1.03% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding a Dock sidebar feature with TUI controls, which aligns with the changeset's primary focus.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The PR description provides a clear summary of changes, mentions testing approach, and includes a detailed cubic-generated summary with feature breakdown.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat-control-deck

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps

greptile-apps Bot commented Apr 28, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR introduces the Dock right-sidebar panel: a configurable list of TUI controls (project/global .cmux/dock.json), replacing the previous FeedPanelView under the persisted .feed mode key, plus a new cmux feed tui CLI command for keyboard-first Feed interaction.

Two issues need attention before merge:

  • Snapshot boundary violation (DockPanelView.swift): DockControlSectionView and DockTerminalView hold @ObservedObject var runtime: DockControlRuntime below a ForEach row boundary, directly violating the team rule against live ObservableObject references inside ForEach rows. Any @Published change (e.g. panel on restart) can trigger full-row re-renders and the CPU spin-loop pattern seen in issue Nightly freezes: sidebar LazyVStack layout loop pegs main thread at 100% CPU, deadlocks CLI #2586.
  • ESC swallows next keypress (CLI/cmux.swift): The TUI escape-sequence reader performs a blocking read(2) after receiving byte 27; with VMIN=1, VTIME=0 raw mode, a lone ESC press will consume the subsequent keypress silently.

Confidence Score: 3/5

Not safe to merge as-is due to a snapshot boundary violation that can cause a CPU spin loop and a TUI input bug that drops keypresses.

Two P1 findings: the @ObservedObject-inside-ForEach pattern is explicitly prohibited by the team's CLAUDE.md rule (risk of 100% CPU spin loop identical to issue #2586), and the ESC key blocking causes silent keypress loss in the new TUI. Both affect core paths introduced by this PR.

Sources/DockPanelView.swift (snapshot boundary) and CLI/cmux.swift (ESC key handling, CJK truncation)

Important Files Changed

Filename Overview
Sources/DockPanelView.swift New 717-line file introducing DockControlsStore, DockControlRuntime, and DockPanelView; contains a snapshot-boundary violation (@ObservedObject inside ForEach) and a deprecated onChange form.
CLI/cmux.swift Adds cmux feed tui with ~420 lines of TUI logic; ESC key handling will consume the next keypress when ESC is pressed alone, and truncateForTerminal uses character count instead of terminal column width.
Sources/MainWindowFocusController.swift Adds dockHost registration and wires Dock focus into the existing .feed mode focus path; pattern mirrors the existing feedHost approach correctly.
Sources/GhosttyTerminalView.swift Adds TerminalSurfaceFocusPlacement enum and focusPlacement property to TerminalSurface; correctly gates dock-surface focus routing through the new placement flag.
Sources/Feed/FeedCoordinator.swift Adds payload serialization to feedItemToDict for all payload variants, enabling the feed.list socket API to surface rich data for the new TUI.
Sources/RightSidebarPanelView.swift Replaces FeedPanelView with DockPanelView for the .feed sidebar mode; raw value preserved for persisted settings compatibility.
Sources/Panels/TerminalPanel.swift Threads focusPlacement through TerminalPanel initializer to TerminalSurface; clean mechanical change.
Resources/Localizable.xcstrings Renames Feed → Dock in existing strings and adds all new Dock-related keys with English and Japanese translations.
docs/dock.md New documentation covering Dock controls, team config, trust model, and naming conventions; accurate and well-structured.

Sequence Diagram

sequenceDiagram
    participant User
    participant DockPanelView
    participant DockControlsStore
    participant DockControlRuntime
    participant TerminalPanel
    participant SocketClient

    DockPanelView->>DockControlsStore: reload(rootDirectory)
    DockControlsStore->>DockControlsStore: resolve() — project / global / built-in
    alt project config and untrusted
        DockControlsStore-->>DockPanelView: trustRequest set → show DockTrustView
        User->>DockPanelView: Trust and Start
        DockPanelView->>DockControlsStore: trustAndReload()
    end
    DockControlsStore->>DockControlRuntime: init(definition, baseDirectory)
    DockControlRuntime->>TerminalPanel: makePanel(definition) focusPlacement=.rightSidebarDock
    DockPanelView->>DockPanelView: render DockControlSectionView per control

    User->>DockPanelView: click Focus button
    DockPanelView->>DockControlRuntime: focus()
    DockControlRuntime->>TerminalPanel: ensureFocus(...)

    User->>SocketClient: cmux feed tui
    SocketClient->>SocketClient: feed.list(pending_only: false)
    SocketClient-->>User: render TUI items sorted pending-first
    User->>SocketClient: keypress e.g. Enter / d / f
    SocketClient->>SocketClient: feed.permission.reply / feed.exit_plan.reply / feed.question.reply
Loading

Comments Outside Diff (4)

  1. Sources/DockPanelView.swift, line 1329-1331 (link)

    P1 Snapshot boundary violation — @ObservedObject inside ForEach

    DockControlSectionView declares @ObservedObject var runtime: DockControlRuntime (and DockTerminalView does the same), placing a live ObservableObject reference below a ForEach row boundary. CLAUDE.md explicitly forbids this pattern: any @Published change on any DockControlRuntime (e.g. panel flipping on restart()) invalidates the whole row set and can cause a re-render feedback loop / CPU spin — the same class of bug that hit the Sessions panel (issue Nightly freezes: sidebar LazyVStack layout loop pegs main thread at 100% CPU, deadlocks CLI #2586). Rows inside ForEach should receive immutable value snapshots and closure bundles only, with no ObservableObject references.

    The same violation is present in DockTerminalView (also @ObservedObject var runtime: DockControlRuntime).

    Context Used: CLAUDE.md (source)

  2. CLI/cmux.swift, line 390-397 (link)

    P1 ESC without a following sequence consumes the next keypress

    After reading byte 27 (ESC), the code immediately calls Darwin.read(STDIN_FILENO, &sequence, 2). If TerminalRawMode is configured with VMIN=1, VTIME=0 (the most common raw-mode setup), this read will block until at least one more byte arrives. Pressing a lone ESC therefore silently swallows the next keypress — e.g. ESC then q eats q and doesn't quit. To handle standalone ESC correctly, the follow-up read must use a short timeout (VMIN=0, VTIME=1 or O_NONBLOCK); if 0 or 1 bytes are returned it should fall through to .ignored without consuming subsequent input.

  3. CLI/cmux.swift, line 439-445 (link)

    P2 truncateForTerminal uses character count, not terminal column width

    sanitized.count counts Swift Character values (Unicode grapheme clusters), not terminal display columns. CJK characters occupy 2 columns each, so a Japanese title or path with CJK content will overflow its visual column limit by roughly half its length and spill into the next line or wrap. Since the app has Japanese localizations and displays user paths/titles, this will produce garbled output for any non-ASCII content. Consider using a wcwidth-based column-width calculator or stringWidth from a terminal-column library before truncating.

  4. Sources/DockPanelView.swift, line 1256-1258 (link)

    P2 onChange(of:) single-closure form deprecated in macOS 14

    The single-trailing-closure form of onChange(of:perform:) is deprecated starting in macOS 14. Since the codebase already has #available(macOS 14.0, *) guards in the same file, consider using the two-parameter form when available:

    .onChange(of: rootDirectory) { _, newValue in
        store.reload(rootDirectory: newValue)
    }

    This avoids a compiler deprecation warning on newer SDKs.

Reviews (1): Last reviewed commit: "Update feed sidebar UI test for Dock" | Re-trigger Greptile

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ce0ce26956

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread CLI/cmux.swift
Comment thread Sources/Feed/FeedCoordinator.swift
Comment thread Sources/DockPanelView.swift Outdated
}

private struct DockControlSectionView: View {
@ObservedObject var runtime: DockControlRuntime

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Remove observable objects from Dock ForEach row subtree

The project guideline in /workspace/cmux/AGENTS.md forbids holding ObservableObject references below ForEach row boundaries because it has previously caused full-row invalidation loops and high CPU in sidebar panels. DockControlSectionView keeps DockControlRuntime as @ObservedObject under a ForEach, reintroducing that exact invalidation risk instead of the required immutable snapshot + action closure pattern.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (4)
docs/feed.md (1)

3-3: Consider tightening wording to avoid “inline” vs “Dock” ambiguity.

The current sentence reads slightly conflicting with the new Dock-first model.

Doc wording tweak
-Feed is cmux's inline surface for AI agent decisions. The keyboard-first Feed TUI can run in the right-sidebar [Dock](dock.md) with `cmux feed tui`. It shows three things that need a human response:
+Feed is cmux’s AI-agent decision surface. The keyboard-first Feed TUI runs in the right-sidebar [Dock](dock.md) via `cmux feed tui`. It shows three things that need a human response:
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/feed.md` at line 3, The wording "Feed is cmux's inline surface for AI
agent decisions" conflicts with the Dock-first model—locate the sentence
containing "Feed is cmux's inline surface for AI agent decisions" and the
following mention of "cmux feed tui" and rephrase to remove "inline" ambiguity
(for example: "Feed is cmux's surface for AI agent decisions; the keyboard-first
Feed TUI can run in the right‑sidebar Dock via `cmux feed tui`"), keeping the
same meaning but clarifying that Feed is a UI surface that can run inline or in
the Dock.
Sources/MainWindowFocusController.swift (1)

354-359: Extract duplicated feed/dock host focus logic into one helper.

The same feed/dock focusing sequence is implemented twice (Line 354 and Line 553 paths). Pulling it into a private helper will reduce drift risk.

♻️ Suggested refactor
@@
         case .feed:
-            if focusFirstItem {
-                feedHost?.focusFirstItemFromCoordinator()
-                dockHost?.focusFirstItemFromCoordinator()
-            }
-            modeResult = feedHost?.focusHostFromCoordinator() == true ||
-                dockHost?.focusHostFromCoordinator() == true
+            modeResult = focusFeedLikeHostsFromCoordinator(focusFirstItem: focusFirstItem)
@@
         case .feed:
-            feedHost?.focusFirstItemFromCoordinator()
-            dockHost?.focusFirstItemFromCoordinator()
-            result = feedHost?.focusHostFromCoordinator() == true ||
-                dockHost?.focusHostFromCoordinator() == true
+            result = focusFeedLikeHostsFromCoordinator(focusFirstItem: true)
         }
@@
+    private func focusFeedLikeHostsFromCoordinator(focusFirstItem: Bool) -> Bool {
+        if focusFirstItem {
+            feedHost?.focusFirstItemFromCoordinator()
+            dockHost?.focusFirstItemFromCoordinator()
+        }
+        return feedHost?.focusHostFromCoordinator() == true ||
+            dockHost?.focusHostFromCoordinator() == true
+    }

Also applies to: 554-557

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

In `@Sources/MainWindowFocusController.swift` around lines 354 - 359, Extract the
duplicated feed/dock focusing sequence into a private helper method (e.g. a
method on MainWindowFocusController like applyFocusSequence(focusFirstItem:
Bool) -> Bool) that performs the conditional calls to
feedHost?.focusFirstItemFromCoordinator(),
dockHost?.focusFirstItemFromCoordinator() when focusFirstItem is true, then
returns the combined result of feedHost?.focusHostFromCoordinator() == true ||
dockHost?.focusHostFromCoordinator() == true; replace the duplicated blocks that
set modeResult with calls to this new helper in both locations.
Sources/DockPanelView.swift (2)

391-406: Consider logging encoding failures in trust descriptor generation.

Line 394 silently falls back to empty Data() if encoding fails. While unlikely for valid DockControlDefinition arrays, a silent failure here could cause unexpected trust behavior. Consider logging the error in DEBUG builds.

🛠️ Suggested improvement
 private static func trustDescriptor(for resolution: DockConfigResolution) -> CmuxActionTrustDescriptor {
     let encoder = JSONEncoder()
     encoder.outputFormatting = [.sortedKeys]
-    let data = (try? encoder.encode(DockConfigFile(controls: resolution.controls))) ?? Data()
+    let data: Data
+    do {
+        data = try encoder.encode(DockConfigFile(controls: resolution.controls))
+    } catch {
+        `#if` DEBUG
+        cmuxDebugLog("DockControlsStore: trust descriptor encoding failed: \(error)")
+        `#endif`
+        data = Data()
+    }
     let commandFingerprint = String(data: data, encoding: .utf8) ?? ""
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/DockPanelView.swift` around lines 391 - 406, The JSON encoding in
trustDescriptor(for resolution: DockConfigResolution) currently swallows errors
by using (try? encoder.encode(...)) ?? Data(), so update this function to
capture the thrown error when encoding DockConfigFile(controls:
resolution.controls) fails and log it in debug builds (e.g., using `#if` DEBUG)
before falling back; specifically, replace the try? pattern with do/catch around
encoder.encode, log the caught error along with context (resolution.sourceURL or
resolution.baseDirectory) via your existing logging facility, and then continue
to use an empty Data() as the fallback so behavior remains unchanged in release
builds.

362-369: Consider documenting the file-to-parent fallback behavior.

When rawPath points to an existing file (not directory), this returns the parent directory. This is a reasonable convenience, but the implicit behavior could surprise callers who expect a file path to fail validation.

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

In `@Sources/DockPanelView.swift` around lines 362 - 369, Add a clear doc comment
above the private static func existingDirectory(_ rawPath: String) explaining
its fallback behavior: that it expands tildes, checks existence, returns nil
when path doesn't exist, returns the directory if rawPath is itself a directory,
and — importantly — when rawPath points to an existing file it returns the
file's parent directory (not nil). Reference the function name existingDirectory
and parameter rawPath in the comment so callers understand this implicit
file-to-parent fallback and expected return semantics.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@CLI/cmux.swift`:
- Around line 17560-17588: renderFeedTUI currently always displays
items.prefix(bodyRows) so when selectedIndex exceeds bodyRows the highlighted
selection can scroll off-screen; change rendering to compute a sliding window
(start..end) of items centered on selectedIndex (clamped to 0..max(0,
items.count-bodyRows)) and iterate over items[start..<min(items.count,
start+bodyRows]) instead of items.prefix(bodyRows), updating the displayed index
math so the selected row maps to the correct printed line; ensure
feedTUIItem(in:at:) and truncateForTerminal continue to receive the actual
selected item and statusLine uses selectedHelp correctly for the visible
selection.
- Around line 17371-17421: The parser drops the multi-select flag so
multi-select questions are handled as single-choice; update FeedTUIItem and
FeedTUIOption to include a multiSelect/allowMultiple Bool (or similar) and
populate it in FeedTUIItem.parse from the incoming payload (e.g., the question
payload key that indicates multi-select), ensure questionOptions preserves that
flag, then update the TUI resolver logic that submits answers (the resolver that
reads FeedTUIItem.questionOptions and sends request ids) to either (A) accept
and send an array of selected ids for multiSelect questions or (B) block/disable
resolution for items where multiSelect == true until UI selection for multiple
choices is implemented—make the change in the parse(_:), FeedTUIItem,
FeedTUIOption, and the resolver/submit path that currently sends a single option
id.

In `@Sources/Feed/FeedCoordinator.swift`:
- Around line 546-561: The .question branch in FeedCoordinator.swift currently
only serializes the first question (case .question, variables requestId and
questions) which drops questions[1...]; update this block to also produce a
backward-compatible "questions" array in dict by mapping every question in
questions into a dictionary (including prompt, multiSelect and options) while
keeping the existing top-level "question_prompt", "question_multi_select", and
"question_options" fields derived from firstQuestion for compatibility; ensure
each option mapping (option.id, option.label, optional option.description) is
reused for all questions so socket consumers receive the full questions payload.

In `@Sources/GhosttyTerminalView.swift`:
- Around line 11181-11209: The right-sidebar dock fast-path in the if block that
checks surfaceView.terminalSurface and focusPlacement == .rightSidebarDock
returns early and bypasses the shared focus guards (e.g.,
respectForeignFirstResponder and the search-focus restoration path); update this
block in ensureFocus so that before returning you invoke the same shared-guard
logic used by the non-dock path: call the existing respectForeignFirstResponder
check (and act on its result) and run the search-focus restoration/ownership
checks (the code paths that call reassertTerminalSurfaceFocus and
isSurfaceViewFirstResponder) instead of an unconditional return, or move the
return to after those checks so docked terminals respect foreign first
responders and search-focus state.

---

Nitpick comments:
In `@docs/feed.md`:
- Line 3: The wording "Feed is cmux's inline surface for AI agent decisions"
conflicts with the Dock-first model—locate the sentence containing "Feed is
cmux's inline surface for AI agent decisions" and the following mention of "cmux
feed tui" and rephrase to remove "inline" ambiguity (for example: "Feed is
cmux's surface for AI agent decisions; the keyboard-first Feed TUI can run in
the right‑sidebar Dock via `cmux feed tui`"), keeping the same meaning but
clarifying that Feed is a UI surface that can run inline or in the Dock.

In `@Sources/DockPanelView.swift`:
- Around line 391-406: The JSON encoding in trustDescriptor(for resolution:
DockConfigResolution) currently swallows errors by using (try?
encoder.encode(...)) ?? Data(), so update this function to capture the thrown
error when encoding DockConfigFile(controls: resolution.controls) fails and log
it in debug builds (e.g., using `#if` DEBUG) before falling back; specifically,
replace the try? pattern with do/catch around encoder.encode, log the caught
error along with context (resolution.sourceURL or resolution.baseDirectory) via
your existing logging facility, and then continue to use an empty Data() as the
fallback so behavior remains unchanged in release builds.
- Around line 362-369: Add a clear doc comment above the private static func
existingDirectory(_ rawPath: String) explaining its fallback behavior: that it
expands tildes, checks existence, returns nil when path doesn't exist, returns
the directory if rawPath is itself a directory, and — importantly — when rawPath
points to an existing file it returns the file's parent directory (not nil).
Reference the function name existingDirectory and parameter rawPath in the
comment so callers understand this implicit file-to-parent fallback and expected
return semantics.

In `@Sources/MainWindowFocusController.swift`:
- Around line 354-359: Extract the duplicated feed/dock focusing sequence into a
private helper method (e.g. a method on MainWindowFocusController like
applyFocusSequence(focusFirstItem: Bool) -> Bool) that performs the conditional
calls to feedHost?.focusFirstItemFromCoordinator(),
dockHost?.focusFirstItemFromCoordinator() when focusFirstItem is true, then
returns the combined result of feedHost?.focusHostFromCoordinator() == true ||
dockHost?.focusHostFromCoordinator() == true; replace the duplicated blocks that
set modeResult with calls to this new helper in both locations.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 12d3beed-7a60-468a-ad22-b806d23334ec

📥 Commits

Reviewing files that changed from the base of the PR and between 822e4e3 and 9dfac06.

📒 Files selected for processing (14)
  • CLI/cmux.swift
  • GhosttyTabs.xcodeproj/project.pbxproj
  • Resources/Localizable.xcstrings
  • Sources/AppDelegate.swift
  • Sources/DockPanelView.swift
  • Sources/Feed/FeedCoordinator.swift
  • Sources/GhosttyTerminalView.swift
  • Sources/KeyboardShortcutSettings.swift
  • Sources/MainWindowFocusController.swift
  • Sources/Panels/TerminalPanel.swift
  • Sources/RightSidebarPanelView.swift
  • cmuxUITests/FeedSidebarUITests.swift
  • docs/dock.md
  • docs/feed.md

Comment thread CLI/cmux.swift
Comment thread CLI/cmux.swift Outdated
Comment thread Sources/Feed/FeedCoordinator.swift
Comment thread Sources/GhosttyTerminalView.swift

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8211dff0c4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread CLI/cmux.swift
Comment thread CLI/cmux.swift
Comment thread CLI/cmux.swift Outdated
Comment on lines +17572 to +17573
for (index, item) in items.prefix(bodyRows).enumerated() {
let selected = index == selectedIndex

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Render a scroll window for selected feed row

The list view always renders items.prefix(bodyRows) from index 0 while navigation updates selectedIndex across the full array. Once there are more rows than fit the terminal, selection can move off-screen and Enter acts on an item the user cannot see, making approvals/error handling unreliable when Feed history grows.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b535af8582

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread CLI/cmux.swift Outdated
Comment on lines +17528 to +17530
case .once, .always, .all, .bypass, .manual, .autoAccept, .ultraplan, .number(_):
if let item = feedTUIItem(in: items, at: selectedIndex) {
statusLine = try resolveFeedTUIItem(item, key: key, client: client, rawMode: &rawMode)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Gate feed hotkeys by selected card kind

This branch routes .once/.always/.all/.bypass/.manual/.autoAccept/.ultraplan/.number to resolveFeedTUIItem for every pending card type, but the per-kind resolvers treat unknown keys as defaults (for example, permission falls back to once). In practice, pressing keys like 1, m, or u while a permission row is selected can silently approve it instead of being ignored, which is a dangerous accidental action path.

Useful? React with 👍 / 👎.

Comment thread CLI/cmux.swift

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cmuxUITests/FeedSidebarUITests.swift`:
- Around line 213-219: The early-return currently checks app.state against
.runningForeground and .runningBackground together; change this to handle them
separately in the test in FeedSidebarUITests.swift: if app.state ==
.runningForeground return immediately; else if app.state == .runningBackground
call app.activate(), then wait for app.wait(for: .runningForeground, timeout:
<shortTimeout>) before proceeding (so subsequent typeKey() calls can send
keyboard input reliably); otherwise (not running) continue to wait as before
with XCTAssertTrue(app.wait(for: .runningForeground, timeout: 15), ...). Use the
existing app variable and state checks (app.state, app.activate(),
app.wait(for:timeout:)) to implement this flow.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 38397fef-7ebe-4a5c-8a7c-eec23d3b7b82

📥 Commits

Reviewing files that changed from the base of the PR and between 8211dff and b535af8.

📒 Files selected for processing (1)
  • cmuxUITests/FeedSidebarUITests.swift

Comment thread cmuxUITests/FeedSidebarUITests.swift Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@CLI/cmux.swift`:
- Around line 17871-17880: In readFeedTUIFeedbackPrompt, re-creating
TerminalRawMode() can fail silently and leave rawMode nil; update the function
to verify the new TerminalRawMode was created successfully (check the result of
TerminalRawMode() before assigning to rawMode), and if it fails log an error or
throw/propagate an error so the caller can handle it; specifically add a
conditional around the TerminalRawMode() construction in
readFeedTUIFeedbackPrompt to detect failure and either restore previous state,
write a clear processLogger/error message, or return an error indicator so
readFeedTUIKey won't run without a valid raw mode.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4cdf9fa8-6232-4aef-ab7a-66c0a1cf204f

📥 Commits

Reviewing files that changed from the base of the PR and between b535af8 and 148f4b2.

📒 Files selected for processing (3)
  • CLI/cmux.swift
  • Sources/DockPanelView.swift
  • docs/dock.md
✅ Files skipped from review due to trivial changes (1)
  • docs/dock.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • Sources/DockPanelView.swift

Comment thread CLI/cmux.swift

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 148f4b2b00

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread CLI/cmux.swift Outdated
case .number(let index):
option = feedTUIOption(in: item.questionOptions, at: index - 1)
default:
option = item.questionOptions.first

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Ignore deny key when resolving question cards

When a question item is selected, pressing d is routed to resolveFeedTUIItem with key .deny, but this switch falls through to the default branch and submits questionOptions.first as the answer. In practice, a user trying to deny a question can unintentionally send the first option back to the agent, which is a dangerous accidental action on pending prompts.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cf281139f9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Sources/DockPanelView.swift Outdated
environment["CMUX_DOCK_CONTROL_TITLE"] = definition.title

return TerminalPanel(
workspaceId: UUID(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Use real workspace context for Dock terminals

Creating each Dock control with workspaceId: UUID() gives the child terminal a synthetic CMUX_WORKSPACE_ID that does not exist in TabManager. TerminalSurface propagates that ID into the shell environment, and many cmux CLI commands default to CMUX_WORKSPACE_ID when no --workspace is passed, so running cmux commands inside Dock controls can fail with workspace-not-found or target the wrong context. Dock surfaces should inherit the active workspace ID (or avoid exporting a fake one) so commands behave like normal in-app terminals.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (1)
cmuxUITests/FeedSidebarUITests.swift (1)

302-315: ⚠️ Potential issue | 🟠 Major

Activate the app when launch leaves it backgrounded.

Returning at Lines 309-310 for .runningBackground still leaves the later typeKey() steps dependent on a non-foreground app.

⌨️ Suggested change
-        if app.state == .runningForeground || app.state == .runningBackground {
+        if app.state == .runningBackground {
+            app.activate()
+            _ = app.wait(for: .runningForeground, timeout: 2)
+            return
+        }
+        if app.state == .runningForeground {
             return
         }
         XCTAssertTrue(
             app.wait(for: .runningForeground, timeout: 15),
             "cmux failed to launch for Feed UI test. state=\(app.state.rawValue)"
In macOS XCTest UI tests, are `XCUIApplication.typeKey(...)` / `XCUIElement.typeKey(...)` reliable when the application is in `.runningBackground`, or should the test call `activate()` and wait for `.runningForeground` first?
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmuxUITests/FeedSidebarUITests.swift` around lines 302 - 315, The helper
launchAndEnsureUsable currently returns early if app.state is
.runningBackground, which can leave subsequent typeKey/type interactions
failing; change the logic in launchAndEnsureUsable (the function calling
app.launch()) to call app.activate() and then wait for .runningForeground (using
app.wait(for: .runningForeground, timeout: ...)) when the app is
.runningBackground instead of returning immediately, ensuring the app is
foregrounded before proceeding with UI interactions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cmuxUITests/FeedSidebarUITests.swift`:
- Around line 257-267: The predicate in waitForDockPortalToLeaveVisibleSidebar
currently treats missing or non-numeric portal stats as zero via
integerValue(in:key:), causing false passes; change the logic to treat
missing/non-numeric values as failures by either modifying integerValue(in:key:)
to return an optional (or adding integerValueStrict(in:key:)) and use that to
ensure the keys "visible_invalid_anchor_entry_count" and
"visible_orphan_terminal_subview_count" exist and parse as integers before
comparing to 0; apply the same fix to the similar check at lines 292-299 so the
XCTNSPredicateExpectation fails if a key is absent or not numeric instead of
defaulting to 0.

In `@Sources/DockPanelView.swift`:
- Around line 46-58: Replace the hard-coded debugDescription strings used in the
DecodingError.dataCorruptedError for normalizedID and normalizedCommand with
localized strings (use String(localized: "dockcontrol.id.blank", defaultValue:
"Dock control id must not be blank") and String(localized:
"dockcontrol.command.blank", defaultValue: "Dock control command must not be
blank")); update the two calls that throw in DockPanelView (the guards
referencing normalizedID, normalizedCommand, container, and
DecodingError.dataCorruptedError) to pass these localized strings so
error.localizedDescription in DockErrorView is localizable, and add the matching
keys to Resources/Localizable.xcstrings for English and Japanese.
- Around line 532-537: The rows created by ForEach currently pass
DockControlRuntime instances into DockControlSectionView and DockTerminalView
causing those child views to observe an ObservableObject inside the row subtree;
instead, capture an immutable snapshot of the per-row state and a bundle of
action closures and pass only those into the row. Concretely, replace passing
`runtime` (DockControlRuntime) into DockControlSectionView/DockTerminalView with
a value struct (e.g. DockControlRowSnapshot containing id, title, status, etc.,
and terminalHeight from heights[index]) plus an actions struct or individual
closures (open/close/toggle/...); update DockControlSectionView and
DockTerminalView to accept the snapshot and closures (removing any
`@ObservedObject/`@Bindable usage) and invoke closures for events. Apply the same
refactor for the other occurrences that currently pass DockControlRuntime into
row views so all ForEach rows receive immutable snapshots + action closures
only.

---

Duplicate comments:
In `@cmuxUITests/FeedSidebarUITests.swift`:
- Around line 302-315: The helper launchAndEnsureUsable currently returns early
if app.state is .runningBackground, which can leave subsequent typeKey/type
interactions failing; change the logic in launchAndEnsureUsable (the function
calling app.launch()) to call app.activate() and then wait for
.runningForeground (using app.wait(for: .runningForeground, timeout: ...)) when
the app is .runningBackground instead of returning immediately, ensuring the app
is foregrounded before proceeding with UI interactions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2830cdb2-514b-4569-86eb-bb6a667aca8d

📥 Commits

Reviewing files that changed from the base of the PR and between 148f4b2 and 56b1067.

📒 Files selected for processing (4)
  • Sources/DockPanelView.swift
  • Sources/RightSidebarPanelView.swift
  • Sources/TerminalWindowPortal.swift
  • cmuxUITests/FeedSidebarUITests.swift

Comment thread cmuxUITests/FeedSidebarUITests.swift
Comment thread Sources/DockPanelView.swift
Comment thread Sources/DockPanelView.swift Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c85cd8c464

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Sources/Feed/FeedCoordinator.swift

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f6d9be9d7b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Resources/feed-tui/index.ts
Comment thread Resources/feed-tui/index.ts
Comment thread Resources/feed-tui/index.ts Outdated
Comment on lines +581 to +583
if (!option) {
this.statusMessage = "No option for selected question.";
break;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Allow OpenTUI to resolve optionless question cards

When a pending question has no question_options, this branch just reports "No option for selected question" and exits without sending any feed.question.reply. Feed supports question cards that do not provide predefined options, so those requests become unresolvable from the default OpenTUI Dock flow and can block agent workflows waiting on a user answer.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f719ecec73

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread CLI/cmux.swift

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3feb21f4b0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Sources/DockPanelView.swift Outdated
throw DecodingError.dataCorruptedError(
forKey: .id,
in: container,
debugDescription: "Dock control id must not be blank"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Localize dock config validation errors

DockControlDefinition.init(from:) throws DecodingError messages with hardcoded English ("Dock control id must not be blank" / command equivalent), and DockControlsStore.reload surfaces those directly via error.localizedDescription in DockErrorView, so invalid dock.json entries show untranslated UI text; this violates the explicit localization rule in /workspace/cmux/AGENTS.md for user-facing strings.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ab827b6a51

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Sources/DockPanelView.swift
Comment thread CLI/cmux.swift Outdated
Comment on lines +18091 to +18094
case 27:
var sequence = [UInt8](repeating: 0, count: 2)
let count = Darwin.read(STDIN_FILENO, &sequence, 2)
guard count == 2, sequence[0] == 91 else { return .ignored }

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Avoid blocking on bare Escape in legacy Feed TUI

The legacy feed tui input parser does a blocking read(..., 2) immediately after seeing byte 27 (Escape). If the user presses Esc by itself (or an incomplete escape sequence arrives), the loop blocks waiting for two extra bytes and the TUI appears frozen until more input is typed. This impacts fallback mode users whenever OpenTUI is unavailable.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ddf163b7d5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Resources/feed-tui/index.ts
# Conflicts:
#	.github/swift-file-length-budget.tsv
#	Sources/MainWindowFocusController.swift

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c1b3228163

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Sources/GhosttyTerminalView.swift Outdated
Comment on lines +7204 to +7206
if let mode = RightSidebarMode.modeShortcut(for: event), let window {
_ = AppDelegate.shared?.focusRightSidebarInActiveMainWindow(mode: mode, focusFirstItem: true, preferredWindow: window)
return

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Gate sidebar mode shortcuts by focus context

This unconditional branch captures every switchRightSidebarTo* key while any terminal surface is focused, so combos like the default Ctrl+1..5 are swallowed in normal workspace terminals and never reach terminal apps. Even though AppDelegate now context-gates these shortcuts, this new GhosttyNSView.keyDown path bypasses that guard and still forces focusRightSidebarInActiveMainWindow, causing unintended sidebar mode switches and input loss outside sidebar-focused responders.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5bb4ac1aff

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Sources/DockPanelView.swift Outdated
Comment thread Resources/feed-tui/index.ts Outdated
Comment on lines +757 to +758
if (primaryOptions.length === 0) {
return [];

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Ignore option hotkeys for optionless question cards

For question cards with no options, this always returns [] regardless of which action key triggered the reply. Combined with question action routing, pressing numeric option keys (1-0) on optionless questions will submit an empty feed.question.reply instead of being ignored, creating an accidental-resolution path for pending prompts.

Useful? React with 👍 / 👎.

This branch was successfully deployed

1 active deployment
Preview – cmux — 1ff5e2f0 Deployed Apr 29, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant