Skip to content

Add terminal.claudeResumeMode to auto-answer Claude Code's resume prompt - #6487

Open
RubiconPerform wants to merge 12 commits into
manaflow-ai:mainfrom
RubiconPerform:agent/claude-resume-mode
Open

RubiconPerform wants to merge 12 commits into
manaflow-ai:mainfrom
RubiconPerform:agent/claude-resume-mode

Conversation

@RubiconPerform

@RubiconPerform RubiconPerform commented Jun 20, 2026 •

Copy link
Copy Markdown
Contributor

Summary

What? Adds a terminal.claudeResumeMode setting (ask | full | summary, default ask) that lets cmux answer Claude Code's compacted-session resume prompt for you when it auto-resumes an agent.

Why? When cmux auto-resumes a compacted Claude Code session (claude --resume <id>), Claude shows an interactive menu:

Resuming the full session will consume a substantial portion of your usage limits. We recommend resuming from a summary.
❯ 1. Resume from summary (recommended)
  2. Resume full session as-is
  3. Don't ask me again

There is no Claude Code CLI flag or config to skip this (verified against the current Claude Code release), so today you have to hand-pick an option every time cmux restores your panes. This adds a cmux-side setting to drive that menu — the natural home, since cmux owns the pane and already auto-resumes the session.

Behavior

  • ask (default) — unchanged: the prompt is left for you. This is a complete no-op; nothing is armed.
  • full — cmux selects Resume full session as-is.
  • summary — cmux selects Resume from summary.

How it works:

  • Armed only from Workspace.createPanel when a Claude agent is being auto-resumed (restorableAgent.kind == .claude and a resume command/input will run) and the mode isn't .ask.
  • A short, hibernation-style poll (ClaudeResumeAutoResponderController, modeled on AgentHibernationController) reads the pane's rendered text via the existing TerminalSurface.readText and, when the menu appears, sends the keys via the existing TerminalSurface.sendNamedKey (so libghostty encodes the right sequence for the pane's cursor mode).
  • One-shot and bounded: it fires at most once per resume, then disarms; if the menu never appears (session wasn't compacted) it gives up after a short watch window.

Design

  • Pure logic, unit-tested: ClaudeResumeMode (SettingCodable enum, mirrors WorkspaceGroupNewPlacement) and ClaudeResumePrompt (detection + keystroke planning) live in CmuxSettings with no AppKit/ghostty deps. Detection is label-based; selection is option-order independent (it finds the target row and the currently-highlighted row by pointer glyph, then moves to it), so it keeps working if the menu is reordered, and falls back to "first row highlighted" when no glyph is rendered.
  • Thin integration: the controller + the one-line arm in createPanel are the only app-side glue.
  • Fail-safe: detection requires Claude's specific option labels, so if a future Claude release changes that wording the responder simply never fires and the prompt stays interactive — no wrong key is sent. Default ask means zero behavior change unless opted in.

Wired through the usual surfaces: Settings → Terminal Picker (CmuxSettingsUI), settings search index + curated entry, cmux.json schema + template + JSON-path support, and the file-store parser.

Testing

  • Added CmuxSettingsTests/ClaudeResumeModeTests.swift covering: tolerant raw-string parsing + UserDefaults/JSON round-trips; prompt detection (present/absent); keystroke planning for the default menu, a reordered menu, and a no-pointer menu; and the one-shot responder (waits for the menu, fires once, never double-fires, inert in ask).
  • The detection/keystroke logic was exercised directly and all assertions pass.

Checklist

  • Added tests for the behavior change (CmuxSettingsTests)
  • Default (ask) preserves existing behavior
  • Swift file-length budget updated for the touched files
  • Demo video — N/A (a setting + a menu responder; covered by unit tests)

View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.


Summary by cubic

Adds terminal.claudeResumeMode (ask | full | summary) to auto-answer Claude Code’s compacted-session resume menu when cmux auto-resumes a Claude agent. Default is ask; the responder is one‑shot, retries until keys are accepted, and ignores stale menu text.

  • New Features

    • Terminal setting/picker for terminal.claudeResumeMode; searchable and localized; added to schema (with descriptionKey), template, JSON‑path, and file‑store parsing. Strict enum parsing (ask/full/summary only); non‑string or invalid values are logged.
    • Auto-responder for Claude auto-resumes (restorable snapshots and agent‑hook resume bindings): detection requires all three option labels; uses the latest contiguous menu block and ignores stale blocks; pointer-aware and option‑order independent. Bounded round‑robin polling with capped reads; pending responders are bounded until a pane goes live, then watched briefly; retries until keystrokes are accepted, then disarms; one‑shot; safe no‑op if the menu never appears. Wired into Workspace.createPanel.
    • Unit tests cover strict parsing, prompt detection (all labels, stale‑block handling), keystroke planning (default, reordered, no‑pointer), and one‑shot with delivery confirmation.
  • Migration

    • No action needed; default is ask.
    • To auto-select, set terminal.claudeResumeMode to full or summary in Settings → Terminal or in cmux.json.

Written for commit 9a8ed08. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features
    • Added a new “Claude Resume Prompt” Terminal setting to control how cmux handles Claude Code’s compacted-session resume prompt during restore/auto-resume.
    • Includes modes: Ask, Full, and Summary.
  • UI / Configuration
    • Exposed in settings with searchable indexing, persisted as terminal.claudeResumeMode, and included in the default template and configuration schema.
  • Automation
    • When set to Full or Summary, cmux can automatically select the appropriate resume option once.
  • Tests
    • Added tests for mode parsing, prompt detection/keystroke selection, and one-shot responder behavior.

@vercel

vercel Bot commented Jun 20, 2026

Copy link
Copy Markdown

@RubiconPerform is attempting to deploy a commit to the Manaflow Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jun 20, 2026 •

Copy link
Copy Markdown

Review Change Stack

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 persisted claudeResumeMode setting with UI, schema, and storage wiring, plus prompt-detection and auto-resume logic that can send terminal keystrokes during Claude session restore.

Changes

Claude Resume Prompt Auto-Responder

Layer / File(s) Summary
Mode and prompt logic
Packages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ClaudeResumeMode.swift, Packages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ClaudeResumePrompt.swift
ClaudeResumeMode defines three cases with tolerant parsing and encode/decode helpers. ClaudeResumeKey maps keystrokes to named key strings. ClaudeResumePrompt detects the resume menu by label and computes ordered keystrokes to reach the requested option. ClaudeResumeAutoResponder wraps the prompt logic as a one-shot, suppressing output after the first successful evaluation.
Setting storage and schema
Packages/macOS/CmuxSettings/Sources/CmuxSettings/Keys/TerminalCatalogSection.swift, Sources/CmuxSettingsJSONPathSupport.swift, Sources/KeyboardShortcutSettingsFileStore+Template.swift, Sources/KeyboardShortcutSettingsFileStore.swift, web/data/cmux.schema.json
Registers the claudeResumeMode DefaultsKey in TerminalCatalogSection, adds terminal.claudeResumeMode to the supported JSON paths set and default template, extends parseTerminalSection to parse and validate the new key, and adds the property to the web JSON schema.
UI row and navigation
Packages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/TerminalSection.swift, Packages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Navigation/CuratedSettingEntry+Default.swift, Sources/SettingsNavigation.swift, Packages/macOS/CmuxSettingsUI/Tests/CmuxSettingsUITests/SettingsRowAnchorResolutionTests.swift, Resources/Localizable.xcstrings
Adds the @State-backed claudeResumeMode property, picker row, and display name helper to TerminalSection; registers the curated entry and search/anchor mappings in SettingsNavigation; extends the anchor resolution test with the new path; and adds localized strings for the setting labels.
Auto-resume controller and restore hook
Sources/App/ClaudeResumeAutoResponderController.swift, Sources/Workspace.swift, cmux.xcodeproj/project.pbxproj
ClaudeResumeModeSettings reads the persisted mode from UserDefaults. ClaudeResumeAutoResponderController is a @MainActor singleton that arms a repeating DispatchSourceTimer, polls each tracked TerminalPanel's rendered text, evaluates it via ClaudeResumeAutoResponder, sends any produced named keys to the panel, and tears down once all entries are resolved. The Workspace restore path arms the controller for .claude agents when a startup command or input will trigger a resume.
Unit tests
Packages/macOS/CmuxSettings/Tests/CmuxSettingsTests/ClaudeResumeModeTests.swift
Tests tolerant parsing, encode/decode round-trips, menu visibility detection, per-mode keystroke sequences (including reordered options and absent pointer glyph), and one-shot auto-responder fire-once behavior.

Sequence Diagram(s)

sequenceDiagram
  actor User
  participant Workspace
  participant ClaudeResumeAutoResponderController
  participant DispatchSourceTimer
  participant TerminalPanel
  participant ClaudeResumeAutoResponder
  participant ClaudeResumePrompt

  User->>Workspace: restore session with .claude agent
  Workspace->>ClaudeResumeAutoResponderController: arm(panel:mode:now:)
  ClaudeResumeAutoResponderController->>DispatchSourceTimer: start repeating poll timer
  loop Poll until menu visible or deadline
    DispatchSourceTimer-->>ClaudeResumeAutoResponderController: tick(now:) on MainActor
    ClaudeResumeAutoResponderController->>TerminalPanel: snapshot rendered text
    ClaudeResumeAutoResponderController->>ClaudeResumeAutoResponder: evaluate(screen:)
    ClaudeResumeAutoResponder->>ClaudeResumePrompt: isVisible(in:) + keystrokes(for:in:)
    ClaudeResumePrompt-->>ClaudeResumeAutoResponder: [ClaudeResumeKey]?
    ClaudeResumeAutoResponder-->>ClaudeResumeAutoResponderController: nil or keys
  end
  ClaudeResumeAutoResponderController->>TerminalPanel: sendNamedKeyResult(...)
  ClaudeResumeAutoResponderController->>DispatchSourceTimer: stopTimer()
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • manaflow-ai/cmux#4777: Touches the same Sources/Workspace.swift restore path used here to decide whether Claude startup input/command handling should run.
  • manaflow-ai/cmux#6196: Extends the same settings search/index and anchor-mapping structures that this PR updates for terminal.claudeResumeMode.

Suggested reviewers

  • lawrencecchen

Poem

🐇 Hop, hop, the resume menu glows,
Ask, full, or summary — the chooser knows.
One little timer, then a key or two,
And Claude resumes with a gentle boo-hoo.
The burrow is quiet, the session's in flight,
A bunny-approved auto-response tonight.


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (6 errors, 1 warning)

Check name Status Explanation Resolution
Cmux Swift Blocking Runtime ❌ Error FAIL: Sources/App/ClaudeResumeAutoResponderController.swift adds a shipped DispatchSourceTimer polling loop plus DispatchQueue.main.async/MainActor.assumeIsolated hops to wait for terminal readiness. Replace the timer/polling with a real signal or callback from terminal output/update events or pane lifecycle; avoid main-queue hops for synchronization.
Cmux Algorithmic Complexity ❌ Error ClaudeResumeAutoResponderController.tick rebuilds Array(entries.keys) every 0.4s over a pane-scalable dictionary, so the hot poll path is still O(N) per tick. Cache a ring/list of armed pane IDs and advance it on arm/remove so each tick only visits the next batch instead of copying all entries.keys.
Cmux Swift Concurrency ❌ Error New app code adds a private DispatchQueue and DispatchSourceTimer polling loop in ClaudeResumeAutoResponderController. Replace the timer/queue poller with structured concurrency tied to pane lifecycle or a terminal-output/update signal, and keep any UI hop minimal.
Cmux Full Internationalization ❌ Error The new xcstrings entries only have en/ja translations, but the catalog supports 20 locales, so 18 locales are missing. Add translated entries for all supported locales (ar, bs, da, de, es, fr, it, km, ko, nb, pl, pt-BR, ru, th, tr, uk, zh-Hans, zh-Hant).
Cmux Architecture Rethink ❌ Error Adds a shipped 0.4s DispatchSourceTimer poll/hop to read terminal text and send keys, which is exactly the timing patch the rule forbids. Move prompt handling to a real terminal-output/lifecycle signal owned by the pane/controller; remove timer polling and main-queue hops from runtime code.
Cmux No Ambient Global State ❌ Error FAIL: Sources/App/ClaudeResumeAutoResponderController.swift:30 adds static let shared; line 6 adds a caseless namespace helper. Both violate no-ambient-global-state. Move responder ownership to an injected instance created at the app seam (e.g. Workspace/App coordinator) and replace ClaudeResumeModeSettings with a private or injected settings reader.
Docstring Coverage ⚠️ Warning Docstring coverage is 20.69% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (18 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the main change: adding terminal.claudeResumeMode to auto-answer Claude Code’s resume prompt.
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.
Cmux Swift Actor Isolation ✅ Passed New value types are pure Sendable models; UI/controller code stays on @MainActor, with no new background access to UI-bound stores or shared mutable Sendable state.
Cmux Browser Automation Off-Main ✅ Passed PR only adds Claude resume settings/controller files; no changes to TerminalController or ControlCommandExecutionPolicy browser routing, so the rule isn’t implicated.
Cmux Expensive Synchronous Load ✅ Passed The PR only arms a main-actor responder and reads live terminal text; it adds no new RestorableAgentSessionIndex.load() or other agent-history sync load on an interactive path.
Cmux Cache Substitution Correctness ✅ Passed New responder reads live terminal text via fresh snapshot APIs, and claudeResumeMode is loaded from managed UserDefaults with file-change refresh; no fresh persistence/snapshot read was replaced...
Cmux No Hacky Sleeps ✅ Passed No TypeScript/JS/shell runtime file was changed; the only delay/polling logic is in Swift, which this check explicitly excludes.
Cmux Swift @Concurrent ✅ Passed No changed Swift code adds or misuses @concurrent/nonisolated async; the new controller stays @MainActor and uses an explicit hop back to the main actor.
Cmux Swift File And Package Boundaries ✅ Passed New feature logic lives in small CmuxSettings package types; app-target code is thin glue, and no new production Swift file exceeds 400 lines or mixes major responsibilities.
Cmux Swiftpm Lockfiles ✅ Passed Root Xcode Package.resolved is included, and no changed cmux-owned .gitignore ignores Package.resolved; no lockfile-policy violation found.
Cmux Swift Logging ✅ Passed No added or changed production logging appears in the diff; touched files only add non-logging code, and the existing Logger is already nonisolated private let.
Cmux User-Facing Error Privacy ✅ Passed PASS: The diff adds only settings labels/help text and internal invalid-setting logging; no new user-facing errors, alerts, or recovery copy expose sensitive implementation details.
Cmux Swiftui State Layout ✅ Passed Only adds another DefaultsValueModel in an existing eager SettingsCard; no GeometryReader, lazy list boundary, or render-time state write.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed The PR adds only terminal-pane responder/settings code; no new NSWindow/NSPanel/WindowGroup code or cmuxAuxiliaryWindowIdentifiers changes were introduced.
Cmux Source Artifacts ✅ Passed The only changed paths are gitlink updates for submodules (ghostty, vendor/bonsplit); no generated logs, temp dirs, build output, or scratch artifacts were added.
Cmux No Test Or Debug Seam In Production Source ✅ Passed Touched production Sources files add only product config/automation APIs; no new #if DEBUG, *ForTesting, or debug/test-only seam was introduced.
Description check ✅ Passed Mostly matches the template with Summary, Testing, and Checklist covered; the Demo Video section and copy/paste Review Trigger block are missing.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@RubiconPerform
RubiconPerform force-pushed the agent/claude-resume-mode branch from c409f36 to 13b8743 Compare June 20, 2026 06:57

@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: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@Packages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Navigation/CuratedSettingEntry`+Default.swift:
- Line 81: The title "Claude Resume Prompt" in the CuratedSettingEntry
initialization with id "claude-resume-mode" is hardcoded as an English string
and needs to be localized. Replace the hardcoded title string with a reference
to a stable localization key (following the naming conventions used elsewhere in
the terminal settings UI), and then add the corresponding localization entries
for that key in the appropriate localization files to ensure the text localizes
properly with the rest of the application.

In `@Sources/App/ClaudeResumeAutoResponderController.swift`:
- Around line 82-104: The tick method is performing a full scan over all entries
in the entries dictionary on every 0.4 second interval, which becomes O(N) work
that degrades performance at scale (~1000 workspaces). Instead of iterating
through Array(entries.keys) to check all entries each tick, implement a bounded
round-robin polling approach by maintaining an index that tracks which entries
have been checked and process only a small, fixed number of entries per tick
(e.g., 1-5 entries). Track the current position in the entries dictionary,
process only entries from that position up to the bounded limit, then advance
the position for the next tick. This ensures all entries eventually get checked
while distributing the terminal snapshot reads across multiple ticks rather than
concentrating them all in one poll cycle.

In `@Sources/KeyboardShortcutSettingsFileStore.swift`:
- Around line 500-506: The code for parsing terminal.claudeResumeMode only logs
invalid values when a string exists but fails to convert to ClaudeResumeMode,
silently ignoring cases where the key exists with a non-string type. Add an else
clause after the `if let raw = jsonString(section["claudeResumeMode"])` check
that verifies if the key exists in the section dictionary. If the key exists but
jsonString returns nil (indicating a non-string type), call
logInvalid("terminal.claudeResumeMode", sourcePath: sourcePath) to ensure
consistent error reporting like adjacent terminal settings.

In `@Sources/Workspace.swift`:
- Around line 1414-1419: The condition for arming the
ClaudeResumeAutoResponderController only checks restorableAgent.kind == .claude,
but it misses the case where a Claude resume binding can exist from the
agent-hook resumeBinding even without a compatible restorableAgent snapshot.
Modify the condition to derive a Claude-resume predicate from both the
restorableAgent snapshot and the restoredBindingLaunch agent-hook binding, then
arm the controller once based on this combined predicate that captures both
binding-based and snapshot-based Claude resumes. This ensures the responder is
armed in all cases where Claude resume work will execute, preventing fallback to
interactive prompt.

In `@web/data/cmux.schema.json`:
- Around line 415-420: The claudeResumeMode property in the schema has a
hardcoded description string but lacks a descriptionKey property for
localization. To fix this, replace the inline description field with a
descriptionKey property that references a localization message key, then add
matching entries for that key in all locale message files listed in
web/i18n/routing.ts (including web/messages/en.json and web/messages/ja.json).
Move the description text from the schema into the locale message files so it
can be properly localized across all supported locales.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: a6b618f5-2d84-4649-8a99-6e982ed62042

📥 Commits

Reviewing files that changed from the base of the PR and between 6d81b40 and c409f36.

⛔ Files ignored due to path filters (1)
  • .github/swift-file-length-budget.tsv is excluded by !**/*.tsv
📒 Files selected for processing (14)
  • Packages/macOS/CmuxSettings/Sources/CmuxSettings/Keys/TerminalCatalogSection.swift
  • Packages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ClaudeResumeMode.swift
  • Packages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ClaudeResumePrompt.swift
  • Packages/macOS/CmuxSettings/Tests/CmuxSettingsTests/ClaudeResumeModeTests.swift
  • Packages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Navigation/CuratedSettingEntry+Default.swift
  • Packages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/TerminalSection.swift
  • Packages/macOS/CmuxSettingsUI/Tests/CmuxSettingsUITests/SettingsRowAnchorResolutionTests.swift
  • Sources/App/ClaudeResumeAutoResponderController.swift
  • Sources/CmuxSettingsJSONPathSupport.swift
  • Sources/KeyboardShortcutSettingsFileStore+Template.swift
  • Sources/KeyboardShortcutSettingsFileStore.swift
  • Sources/SettingsNavigation.swift
  • Sources/Workspace.swift
  • web/data/cmux.schema.json

Comment thread Sources/App/ClaudeResumeAutoResponderController.swift Outdated
Comment thread Sources/KeyboardShortcutSettingsFileStore.swift
Comment thread Sources/Workspace.swift Outdated
Comment thread web/data/cmux.schema.json
@greptile-apps

greptile-apps Bot commented Jun 20, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

Adds terminal.claudeResumeMode (ask | full | summary, default ask) so cmux can automatically answer Claude Code's compacted-session resume menu when it auto-resumes a Claude agent. The default ask is a complete no-op, so existing behavior is fully preserved for users who do not opt in.

  • New ClaudeResumeAutoResponder / ClaudeResumePrompt: pure, unit-tested logic in the CmuxSettings package that detects the resume menu by requiring all three option labels in a contiguous on-screen block, then calculates arrow-key deltas to the target option using the pointer glyph for position awareness — fail-safe if Claude's wording ever changes.
  • New ClaudeResumeAutoResponderController: a bounded DispatchSourceTimer poller (mirrors AgentHibernationController) armed from Workspace.createPanel for both restorable-snapshot and agent-hook-binding resume paths; one-shot per arm, with a 30-minute pending window and 45-second live-watch window.
  • Settings wiring: TerminalCatalogSection, TerminalSection picker, search index, curated entry, cmux.schema.json, cmux.json template, file-store parser, and CmuxSettingsJSONPathSupport all updated; Localizable.xcstrings receives the five new keys with en and ja translations (18 other supported locales are absent — carried from the previous review thread).

Confidence Score: 4/5

Safe to merge; the default ask is a complete no-op and the automation path is gated behind an explicit opt-in setting.

The detection and keystroke-planning logic is pure and well-tested. The one area to revisit is the new static let shared singleton on ClaudeResumeAutoResponderController: it holds meaningful runtime collections and is wired directly from Workspace, which goes against the project's injection model. The PR explicitly acknowledges this mirrors an existing controller — a migration of both should be tracked. Everything else (enum parsing, menu detection, retry logic, settings wiring) looks correct and consistent with codebase conventions.

Sources/App/ClaudeResumeAutoResponderController.swift — the runtime singleton; Resources/Localizable.xcstrings — 18 locales still missing translations for the five new keys.

Important Files Changed

Filename Overview
Sources/App/ClaudeResumeAutoResponderController.swift New @mainactor controller driving the one-shot resume responder via DispatchSourceTimer; introduces a static let shared singleton for runtime state that should be injected rather than globalised.
Packages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ClaudeResumePrompt.swift Pure, AppKit-free detection and keystroke planning for the Claude resume menu; correctly requires all three option labels in a contiguous block and is pointer-order independent.
Packages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ClaudeResumeAutoResponder.swift Simple one-shot responder wrapper; delegates detection to ClaudeResumePrompt and tracks delivery confirmation. No issues found.
Sources/Workspace.swift Adds arm() call after session-restore for both restorable-snapshot and binding-launch Claude resume paths; correctly guarded by kind checks.
Resources/Localizable.xcstrings Five new localization keys added with en + ja translations only; 18 other supported locales (ar, bs, da, de, es, fr, it, km, ko, nb, pl, pt-BR, ru, th, tr, uk, zh-Hans, zh-Hant) have no entries for the new keys.
Packages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/TerminalSection.swift Adds Picker UI for claudeResumeMode with localized labels, accessibilityIdentifier, and proper DefaultsValueModel wiring. No issues found.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant W as Workspace.createPanel
    participant C as ClaudeResumeAutoResponderController
    participant T as DispatchSourceTimer (main)
    participant TC as TerminalController
    participant P as TerminalPanel (surface)
    participant R as ClaudeResumeAutoResponder

    W->>C: arm(panel:) [if Claude agent + auto-resume]
    C->>C: store responder + pendingDeadline + deadlines
    C->>T: startTimerIfNeeded() — 0.4 s repeating

    loop Every 0.4 s (bounded batch, max 24/tick)
        T->>C: tick(now:)
        C->>P: hasLiveSurface?
        alt not live yet
            C-->>C: skip (pending window still open)
        else live
            C->>TC: readTerminalTextForSnapshot(panel, scrollback:false)
            TC-->>C: screen: String?
            C->>R: evaluate(screen:)
            R->>R: ClaudeResumePrompt.keystrokes(for:in:)
            alt menu detected
                R-->>C: [ClaudeResumeKey] (up/down + enter)
                C->>P: sendNamedKeyResult per key
                alt all keys accepted
                    C->>R: confirmDelivered()
                    C->>C: removeEntry — disarm
                else partial failure
                    C-->>C: retry next tick (screen re-read gives updated pointer)
                end
            else menu not visible / already responded
                R-->>C: nil — skip
            end
        end
    end

    note over C,T: Timer cancelled when responders empty or watch window expires
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant W as Workspace.createPanel
    participant C as ClaudeResumeAutoResponderController
    participant T as DispatchSourceTimer (main)
    participant TC as TerminalController
    participant P as TerminalPanel (surface)
    participant R as ClaudeResumeAutoResponder

    W->>C: arm(panel:) [if Claude agent + auto-resume]
    C->>C: store responder + pendingDeadline + deadlines
    C->>T: startTimerIfNeeded() — 0.4 s repeating

    loop Every 0.4 s (bounded batch, max 24/tick)
        T->>C: tick(now:)
        C->>P: hasLiveSurface?
        alt not live yet
            C-->>C: skip (pending window still open)
        else live
            C->>TC: readTerminalTextForSnapshot(panel, scrollback:false)
            TC-->>C: screen: String?
            C->>R: evaluate(screen:)
            R->>R: ClaudeResumePrompt.keystrokes(for:in:)
            alt menu detected
                R-->>C: [ClaudeResumeKey] (up/down + enter)
                C->>P: sendNamedKeyResult per key
                alt all keys accepted
                    C->>R: confirmDelivered()
                    C->>C: removeEntry — disarm
                else partial failure
                    C-->>C: retry next tick (screen re-read gives updated pointer)
                end
            else menu not visible / already responded
                R-->>C: nil — skip
            end
        end
    end

    note over C,T: Timer cancelled when responders empty or watch window expires
Loading

Reviews (12): Last reviewed commit: "Merge remote-tracking branch 'origin/mai..." | Re-trigger Greptile

Comment thread Sources/App/ClaudeResumeAutoResponderController.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: 4

♻️ Duplicate comments (2)
web/data/cmux.schema.json (1)

423-428: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add descriptionKey for terminal.claudeResumeMode schema copy.

At Line 427, the new user-facing description is inline-only and not keyed for locale catalogs, so it cannot be verified across supported locales like other localized schema descriptions.

Small source-of-truth fix
         "claudeResumeMode": {
           "type": "string",
           "enum": ["ask", "full", "summary"],
           "default": "ask",
+          "descriptionKey": "schemaDescriptions.terminal.claudeResumeMode",
           "description": "How cmux answers Claude Code's compacted-session resume prompt when auto-resuming a Claude agent. \"ask\" leaves the interactive prompt for you; \"full\" auto-selects Resume full session as-is; \"summary\" auto-selects Resume from summary."
         },

As per coding guidelines, web/data/**/*.json user-facing schema copy must have locale-specific coverage for every supported locale.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/data/cmux.schema.json` around lines 423 - 428, The "claudeResumeMode"
property in cmux.schema.json has an inline description that lacks locale catalog
support, preventing it from being verified across supported locales. Add a
`descriptionKey` field to the claudeResumeMode property definition that maps to
an appropriate locale catalog key (following the same pattern as other localized
schema descriptions in the file), so the description can be properly tracked and
verified for all supported locales.

Source: Coding guidelines

Sources/KeyboardShortcutSettingsFileStore.swift (1)

501-507: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Log non-string terminal.claudeResumeMode values as invalid.

At Line 501, malformed non-string values are silently ignored, unlike neighboring terminal settings that call logInvalid(...) when the key exists but the type is wrong.

Minimal consistency fix
         if let raw = jsonString(section["claudeResumeMode"]) {
             if let mode = ClaudeResumeMode(rawString: raw) {
                 snapshot.managedUserDefaults[SettingCatalog().terminal.claudeResumeMode.userDefaultsKey] = .string(mode.rawValue)
             } else {
                 logInvalid("terminal.claudeResumeMode", sourcePath: sourcePath)
             }
+        } else if section.keys.contains("claudeResumeMode") {
+            logInvalid("terminal.claudeResumeMode", sourcePath: sourcePath)
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/KeyboardShortcutSettingsFileStore.swift` around lines 501 - 507, The
code in the terminal.claudeResumeMode block silently ignores non-string values
instead of logging them as invalid like other terminal settings do. Add a check
to detect when section["claudeResumeMode"] exists but jsonString() returns nil
(indicating a type mismatch), and call logInvalid("terminal.claudeResumeMode",
sourcePath: sourcePath) in that case. The fix should wrap the existing if-let
chain with an outer check for key existence, so that non-string types are
explicitly logged as invalid before attempting string parsing.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@Packages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ClaudeResumeMode.swift`:
- Around line 20-33: The init?(rawString:) method in ClaudeResumeMode currently
accepts aliases like "manual", "prompt", "full-session", "fullsession",
"full_session", "as-is", and "asis" which are not defined in the schema and
violate the strict contract requiring only ["ask","full","summary"] as valid
values. Modify the switch statement to accept only the exact schema-defined
values: "ask" maps to .ask, "full" maps to .full, and "summary" maps to
.summary, removing all other case branches to ensure invalid configurations are
properly rejected instead of silently accepted.

In
`@Packages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ClaudeResumePrompt.swift`:
- Around line 32-34: The isVisible method uses overly loose substring checks
with screen.contains that can match unrelated terminal content, potentially
triggering accidental key injection in the code at lines 98-102. Replace the
simple contains checks for summaryLabel and fullLabel with a stricter pattern
validation that confirms all three option labels are present together in the
proper menu block structure, not just scattered anywhere on screen. Apply the
same stricter validation logic to the similar visibility check in the code
around lines 57-66.
- Around line 57-69: The code from lines 57-69 performs multiple full passes
through the screen data (split, then map, then filter, followed by separate
firstIndex scans for targetPosition and currentPosition), which is inefficient
in this polling hot path. Refactor this to collapse into a single bounded pass
by combining the line filtering and index tracking logic into one iteration.
Instead of chaining split, map, filter operations and then calling firstIndex
separately, use a single loop or reduce operation that simultaneously builds the
optionLines array, finds the targetPosition where targetLabel appears, and finds
the currentPosition where pointerGlyphs are found in one traversal of the split
lines.

In
`@Packages/macOS/CmuxSettings/Tests/CmuxSettingsTests/ClaudeResumeModeTests.swift`:
- Around line 10-13: The test expectations for ClaudeResumeMode are accepting
invalid aliases that contradict the schema specification. Update the
expectations in the ClaudeResumeModeTests to reject alias values: change the
expectation for rawString "full-session" from expecting .full to expecting nil,
and change the expectation for rawString "manual" from expecting .ask to
expecting nil. This ensures the tests align with the schema contract that only
allows the strict enum values "ask", "full", and "summary".

---

Duplicate comments:
In `@Sources/KeyboardShortcutSettingsFileStore.swift`:
- Around line 501-507: The code in the terminal.claudeResumeMode block silently
ignores non-string values instead of logging them as invalid like other terminal
settings do. Add a check to detect when section["claudeResumeMode"] exists but
jsonString() returns nil (indicating a type mismatch), and call
logInvalid("terminal.claudeResumeMode", sourcePath: sourcePath) in that case.
The fix should wrap the existing if-let chain with an outer check for key
existence, so that non-string types are explicitly logged as invalid before
attempting string parsing.

In `@web/data/cmux.schema.json`:
- Around line 423-428: The "claudeResumeMode" property in cmux.schema.json has
an inline description that lacks locale catalog support, preventing it from
being verified across supported locales. Add a `descriptionKey` field to the
claudeResumeMode property definition that maps to an appropriate locale catalog
key (following the same pattern as other localized schema descriptions in the
file), so the description can be properly tracked and verified for all supported
locales.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0404d4e1-2788-486c-a203-5e67761beded

📥 Commits

Reviewing files that changed from the base of the PR and between c409f36 and 13b8743.

⛔ Files ignored due to path filters (1)
  • .github/swift-file-length-budget.tsv is excluded by !**/*.tsv
📒 Files selected for processing (14)
  • Packages/macOS/CmuxSettings/Sources/CmuxSettings/Keys/TerminalCatalogSection.swift
  • Packages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ClaudeResumeMode.swift
  • Packages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ClaudeResumePrompt.swift
  • Packages/macOS/CmuxSettings/Tests/CmuxSettingsTests/ClaudeResumeModeTests.swift
  • Packages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Navigation/CuratedSettingEntry+Default.swift
  • Packages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/TerminalSection.swift
  • Packages/macOS/CmuxSettingsUI/Tests/CmuxSettingsUITests/SettingsRowAnchorResolutionTests.swift
  • Sources/App/ClaudeResumeAutoResponderController.swift
  • Sources/CmuxSettingsJSONPathSupport.swift
  • Sources/KeyboardShortcutSettingsFileStore+Template.swift
  • Sources/KeyboardShortcutSettingsFileStore.swift
  • Sources/SettingsNavigation.swift
  • Sources/Workspace.swift
  • web/data/cmux.schema.json

Comment thread Packages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ClaudeResumePrompt.swift Outdated
Comment thread Packages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ClaudeResumePrompt.swift Outdated
Comment thread Packages/macOS/CmuxSettings/Tests/CmuxSettingsTests/ClaudeResumeModeTests.swift Outdated
@RubiconPerform

Copy link
Copy Markdown
Contributor Author

Thanks for the reviews — all findings addressed in 2303e538.

CodeRabbit

  • ClaudeResumeMode.init?(rawString:) now accepts only the exact schema enum (ask|full|summary) and rejects non-schema aliases, so invalid config is reported instead of silently accepted.
  • ClaudeResumePrompt.isVisible now requires all three option labels, so the responder never synthesizes keys on incidental terminal output; the keystroke planner is now a single pass over the screen.
  • Non-string terminal.claudeResumeMode values now emit logInvalid(...).
  • Curated settings-search title is now localized (String(localized:)).
  • The auto-responder poll is now a bounded round-robin (cap reads per tick) instead of an O(N) sweep.
  • Arming now also covers agent-hook binding Claude resumes, not just restorable-agent snapshots.

Greptile

  • Dropped the untracked Task.detached inside the DispatchSourceTimer; the tick now hops via DispatchQueue.main + MainActor.assumeIsolated.

Also wired Sources/App/ClaudeResumeAutoResponderController.swift into cmux.xcodeproj (it's an app-target file).

Verified locally on Xcode 16.4 / Swift 6.1.2: cmux-unit TEST BUILD SUCCEEDED, and the CmuxSettings (12) + CmuxSettingsUI anchor-resolution (5) Swift Testing suites pass. The only failing app-host tests in that run are the pre-existing NSAppearance/Aqua dark-mode cases that also fail on clean main.

@RubiconPerform
RubiconPerform force-pushed the agent/claude-resume-mode branch from 2303e53 to ffa9295 Compare June 22, 2026 21:12

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
Packages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ClaudeResumeMode.swift (1)

17-19: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Update stale parser docs to match strict behavior.

Lines 17-19 still describe alias spellings, but the parser now accepts only ask|full|summary.

Proposed doc fix
-    /// Tolerant parse shared by every configuration surface (`cmux.json`,
-    /// `settings.json`, the control socket): trims whitespace and accepts a few
-    /// natural spellings case-insensitively.
+    /// Shared parse for every configuration surface (`cmux.json`,
+    /// `settings.json`, the control socket): trims whitespace and accepts only
+    /// the schema values (`ask`, `full`, `summary`) case-insensitively.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@Packages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ClaudeResumeMode.swift`
around lines 17 - 19, Update the documentation comment for the parser (the
comment block starting with "Tolerant parse shared by every configuration
surface") to accurately reflect the current strict parsing behavior. Remove
references to tolerant parsing, natural spellings, case-insensitivity, and
whitespace trimming since the parser now only accepts the specific values
`ask|full|summary`. Replace the description to clarify that the parser enforces
strict value validation across all configuration surfaces without the flexible
alias handling previously documented.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@Packages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Navigation/CuratedSettingEntry`+Default.swift:
- Line 89: The localization key "settings.terminal.claudeResumeMode" used in the
CuratedSettingEntry initialization is missing from the string catalog. Add a new
entry to Resources/Localizable.xcstrings with the key
"settings.terminal.claudeResumeMode" setting the English string value to "Claude
Resume Prompt" and provide a matching Japanese translation for the ja locale to
ensure proper localization support across both languages.

---

Outside diff comments:
In
`@Packages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ClaudeResumeMode.swift`:
- Around line 17-19: Update the documentation comment for the parser (the
comment block starting with "Tolerant parse shared by every configuration
surface") to accurately reflect the current strict parsing behavior. Remove
references to tolerant parsing, natural spellings, case-insensitivity, and
whitespace trimming since the parser now only accepts the specific values
`ask|full|summary`. Replace the description to clarify that the parser enforces
strict value validation across all configuration surfaces without the flexible
alias handling previously documented.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: ca2fd26e-0d92-4aeb-857b-7864d961e55d

📥 Commits

Reviewing files that changed from the base of the PR and between 13b8743 and 2303e53.

⛔ Files ignored due to path filters (1)
  • .github/swift-file-length-budget.tsv is excluded by !**/*.tsv
📒 Files selected for processing (8)
  • Packages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ClaudeResumeMode.swift
  • Packages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ClaudeResumePrompt.swift
  • Packages/macOS/CmuxSettings/Tests/CmuxSettingsTests/ClaudeResumeModeTests.swift
  • Packages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Navigation/CuratedSettingEntry+Default.swift
  • Sources/App/ClaudeResumeAutoResponderController.swift
  • Sources/KeyboardShortcutSettingsFileStore.swift
  • Sources/Workspace.swift
  • cmux.xcodeproj/project.pbxproj

@RubiconPerform
RubiconPerform force-pushed the agent/claude-resume-mode branch 2 times, most recently from 5d38344 to e951c80 Compare June 22, 2026 21:20

@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: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@Packages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ClaudeResumeMode.swift`:
- Around line 17-19: The documentation comment for the tolerant parser in
ClaudeResumeMode describes accepting "a few natural spellings
case-insensitively," but the actual implementation only accepts exact schema
values (`ask|full|summary`). Update the doc comment to accurately describe the
current behavior: remove the mention of accepting natural spellings and clarify
that only exact case-sensitive schema values are accepted. This will prevent
future confusion and regressions.

In `@Sources/Workspace.swift`:
- Around line 1425-1427: The logic for bindingClaudeResume is too permissive and
currently arms any restored binding with kind matching .claude, but should only
arm for agent-hook resume bindings. Add an isAgentHookBinding guard condition to
the bindingClaudeResume assignment so that it requires both the binding to be
restored with .claude kind AND the binding to be an agent-hook binding,
preventing non-resume Claude launches from triggering the 45 second
synthetic-key watcher.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5335e414-2ef5-40d5-ba91-ba75562b2e7a

📥 Commits

Reviewing files that changed from the base of the PR and between ffa9295 and e951c80.

⛔ Files ignored due to path filters (1)
  • .github/swift-file-length-budget.tsv is excluded by !**/*.tsv
📒 Files selected for processing (15)
  • Packages/macOS/CmuxSettings/Sources/CmuxSettings/Keys/TerminalCatalogSection.swift
  • Packages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ClaudeResumeMode.swift
  • Packages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ClaudeResumePrompt.swift
  • Packages/macOS/CmuxSettings/Tests/CmuxSettingsTests/ClaudeResumeModeTests.swift
  • Packages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Navigation/CuratedSettingEntry+Default.swift
  • Packages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/TerminalSection.swift
  • Packages/macOS/CmuxSettingsUI/Tests/CmuxSettingsUITests/SettingsRowAnchorResolutionTests.swift
  • Sources/App/ClaudeResumeAutoResponderController.swift
  • Sources/CmuxSettingsJSONPathSupport.swift
  • Sources/KeyboardShortcutSettingsFileStore+Template.swift
  • Sources/KeyboardShortcutSettingsFileStore.swift
  • Sources/SettingsNavigation.swift
  • Sources/Workspace.swift
  • cmux.xcodeproj/project.pbxproj
  • web/data/cmux.schema.json

Comment thread Packages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ClaudeResumeMode.swift Outdated
Comment thread Sources/Workspace.swift
@RubiconPerform

Copy link
Copy Markdown
Contributor Author

Follow-up — addressed the remaining open threads in 86aec98:

  • Localization (xcstrings, critical): added settings.terminal.claudeResumeMode and .ask / .full / .subtitle / .summary to Resources/Localizable.xcstrings with en + ja values. The Settings row, search index, and curated entry referenced these keys via String(localized:) but they weren't in the catalog.
  • Localization (schema coverage, major): re-added "descriptionKey": "schemaDescriptions.terminal.claudeResumeMode" to the cmux.schema.json property and added that key to all 20 web/messages/* locales.
  • ClaudeResumeAutoResponderController.swift (Greptile — fire-and-forget Task.detached): already resolved in 2303e538. The Task.detached was removed; the timer tick now hops via DispatchQueue.main + MainActor.assumeIsolated, so there's no untracked task.

All mirrors the merged terminal.scrollSpeed localization. The branch is rebased onto current main and builds clean locally on Xcode 16.4 (cmux-unit TEST BUILD SUCCEEDED; CmuxSettings/CmuxSettingsUI test suites pass).

RubiconPerform and others added 4 commits June 23, 2026 14:19
… resume prompt

When cmux auto-resumes a compacted Claude Code session, Claude shows an
interactive menu ("Resume from summary" / "Resume full session as-is" /
"Don't ask me again") that has no CLI flag or config to skip. This adds a
`terminal.claudeResumeMode` setting so cmux can answer it for you.

- ClaudeResumeMode: SettingCodable enum (ask | full | summary), default `ask`
  (preserves current behavior). New DefaultsKey on TerminalCatalogSection.
- ClaudeResumePrompt: pure, dependency-free detection + keystroke planning —
  label-based, selection-pointer aware, option-order independent, one-shot.
  Fully unit-tested in CmuxSettingsTests.
- ClaudeResumeAutoResponderController: hibernation-style poll that reads the
  resumed pane via TerminalSurface.readText and sends keys via sendNamedKey,
  armed from Workspace.createPanel only for Claude auto-resumes.
- Wiring: Settings → Terminal Picker, settings search index + curated entry,
  cmux.json schema + template + JSON-path support, file-store parser.

Default `ask` is a no-op, so existing behavior is unchanged for anyone who
doesn't opt in.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Restrict ClaudeResumeMode parsing to the exact schema enum (ask|full|summary)
  and reject non-schema aliases, so invalid config is reported instead of
  silently accepted.
- Tighten resume-menu detection to require all three option labels, so the
  responder never synthesizes keys on incidental terminal output.
- Single-pass option parsing in the keystroke planner (polling hot path).
- logInvalid for non-string terminal.claudeResumeMode values.
- Localize the curated settings-search title.
- Bounded round-robin polling in the auto-responder (cap reads per tick) and drop
  the untracked Task.detached (DispatchQueue.main + MainActor.assumeIsolated).
- Arm the responder for agent-hook binding Claude resumes too, not only
  restorable-agent snapshots.
- Wire ClaudeResumeAutoResponderController.swift into cmux.xcodeproj (app target).

Verified on Xcode 16.4 / Swift 6.1.2: `cmux-unit` TEST BUILD SUCCEEDED; the
CmuxSettings (12) and CmuxSettingsUI anchor-resolution (5) Swift Testing suites
pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Add settings.terminal.claudeResumeMode[, .ask, .full, .subtitle, .summary] to
  Resources/Localizable.xcstrings with en + ja values. The Settings UI / search /
  curated entry referenced these keys via String(localized:) but they were absent
  from the string catalog (CodeRabbit critical finding).
- Re-add "descriptionKey": "schemaDescriptions.terminal.claudeResumeMode" to the
  cmux.schema.json property and add that key to all 20 web/messages locales so the
  schema copy has full locale coverage (CodeRabbit major finding).

Mirrors the merged terminal.scrollSpeed localization exactly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…uard

- ClaudeResumeMode.init?(rawString:) doc now states it accepts only the exact
  schema enum values (was stale 'natural spellings').
- bindingClaudeResume now also requires resumeBinding.isAgentHookBinding, so only
  agent-hook resume bindings arm the watcher (non-resume Claude launches don't get
  a 45s synthetic-key watcher).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@RubiconPerform
RubiconPerform force-pushed the agent/claude-resume-mode branch from 1c69a37 to ab0f531 Compare June 23, 2026 18:23
# Conflicts:
#	.github/swift-file-length-budget.tsv
@austinywang

Copy link
Copy Markdown
Contributor

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 24, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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: 2

♻️ Duplicate comments (1)
Packages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ClaudeResumePrompt.swift (1)

35-36: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Require three distinct menu rows before sending keys.

A screen that mentions all three labels in prose still passes isVisible, and this parser then collapses that to a single optionLines row. In that case .full/.summary returns [.enter], so the controller can inject a keystroke into a resumed Claude pane even though the actual menu never appeared. Parse one authoritative menu block and fail closed unless you find three distinct option rows. As per path instructions, "avoid deriving that truth from heuristics like matching titles/labels/text ... and fail closed when the reliable signal is missing."

Also applies to: 64-75

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@Packages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ClaudeResumePrompt.swift`
around lines 35 - 36, The Claude resume detection in ClaudeResumePrompt is too
permissive because isVisible(in:) matches prose containing all three labels and
the parser can collapse that into a single option row, causing send-keys to fire
when no menu is actually present. Update the parsing logic around
isVisible(in:), the option-line extraction, and the .full/.summary handling to
detect one authoritative menu block with three distinct rows, and fail closed by
returning no actionable keys unless those rows are present. Use the existing
symbols isVisible(in:), optionLines, .full, and .summary to locate the flow and
avoid relying on label heuristics.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Sources/App/ClaudeResumeAutoResponderController.swift`:
- Around line 109-113: The navigation-key delivery flow in
ClaudeResumeAutoResponderController’s evaluate/send sequence should fail closed
instead of ignoring send results. Update the loop that calls
panel.sendNamedKeyResult so it stops sending any remaining keys as soon as one
delivery fails, and only remove the entry from entries after the full planned
key sequence has been delivered successfully. Keep the fix localized around
evaluate(screen:) and the sendNamedKeyResult handling so the responder is not
disarmed prematurely.
- Around line 66-80: The polling timer in startTimerIfNeeded() is introducing a
shipped DispatchSourceTimer loop for terminal readiness, which should be
replaced. Update ClaudeResumeAutoResponderController to trigger tick(now:) from
a terminal-output/update signal or a cancellable scheduler/async sequence tied
to the pane lifecycle, and remove the periodic timer-based sampling logic
entirely. Keep the fix localized around startTimerIfNeeded(), timerQueue, and
the tick(now:) entry point so the responder reacts to real updates instead of
polling.

---

Duplicate comments:
In
`@Packages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ClaudeResumePrompt.swift`:
- Around line 35-36: The Claude resume detection in ClaudeResumePrompt is too
permissive because isVisible(in:) matches prose containing all three labels and
the parser can collapse that into a single option row, causing send-keys to fire
when no menu is actually present. Update the parsing logic around
isVisible(in:), the option-line extraction, and the .full/.summary handling to
detect one authoritative menu block with three distinct rows, and fail closed by
returning no actionable keys unless those rows are present. Use the existing
symbols isVisible(in:), optionLines, .full, and .summary to locate the flow and
avoid relying on label heuristics.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 265b2a92-e44d-476b-85f2-badb7592d6eb

📥 Commits

Reviewing files that changed from the base of the PR and between ffa9295 and a2df375.

⛔ Files ignored due to path filters (1)
  • .github/swift-file-length-budget.tsv is excluded by !**/*.tsv
📒 Files selected for processing (15)
  • Packages/macOS/CmuxSettings/Sources/CmuxSettings/Keys/TerminalCatalogSection.swift
  • Packages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ClaudeResumeMode.swift
  • Packages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ClaudeResumePrompt.swift
  • Packages/macOS/CmuxSettings/Tests/CmuxSettingsTests/ClaudeResumeModeTests.swift
  • Packages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Navigation/CuratedSettingEntry+Default.swift
  • Packages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/TerminalSection.swift
  • Packages/macOS/CmuxSettingsUI/Tests/CmuxSettingsUITests/SettingsRowAnchorResolutionTests.swift
  • Resources/Localizable.xcstrings
  • Sources/App/ClaudeResumeAutoResponderController.swift
  • Sources/CmuxSettingsJSONPathSupport.swift
  • Sources/KeyboardShortcutSettingsFileStore+Template.swift
  • Sources/KeyboardShortcutSettingsFileStore.swift
  • Sources/SettingsNavigation.swift
  • Sources/Workspace.swift
  • cmux.xcodeproj/project.pbxproj
💤 Files with no reviewable changes (2)
  • cmux.xcodeproj/project.pbxproj
  • Sources/Workspace.swift

Comment thread Sources/App/ClaudeResumeAutoResponderController.swift
Comment thread Sources/App/ClaudeResumeAutoResponderController.swift Outdated
@vercel

vercel Bot commented Jun 30, 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 Jun 30, 2026 8:49pm

@teamleaderleo teamleaderleo added enhancement New feature or request area: agents Agent integrations (Claude Code, Codex, ACP), agent chat, hooks, status area: settings Settings UI and the config file needs a call Finished and held for a team design or product decision (see #13742 and the gallery in #15427) labels Sep 30, 2026

This branch was successfully deployed

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

Labels

area: agents Agent integrations (Claude Code, Codex, ACP), agent chat, hooks, status area: settings Settings UI and the config file enhancement New feature or request needs a call Finished and held for a team design or product decision (see #13742 and the gallery in #15427)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants