Add terminal.claudeResumeMode to auto-answer Claude Code's resume prompt - #6487
RubiconPerform wants to merge 12 commits into
Conversation
|
@RubiconPerform is attempting to deploy a commit to the Manaflow Team on Vercel. A member of the Team first needs to authorize it. |
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds a persisted ChangesClaude Resume Prompt Auto-Responder
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()
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
Important Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional. ❌ Failed checks (6 errors, 1 warning)
✅ Passed checks (18 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
c409f36 to
13b8743
Compare
There was a problem hiding this comment.
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
⛔ Files ignored due to path filters (1)
.github/swift-file-length-budget.tsvis excluded by!**/*.tsv
📒 Files selected for processing (14)
Packages/macOS/CmuxSettings/Sources/CmuxSettings/Keys/TerminalCatalogSection.swiftPackages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ClaudeResumeMode.swiftPackages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ClaudeResumePrompt.swiftPackages/macOS/CmuxSettings/Tests/CmuxSettingsTests/ClaudeResumeModeTests.swiftPackages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Navigation/CuratedSettingEntry+Default.swiftPackages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/TerminalSection.swiftPackages/macOS/CmuxSettingsUI/Tests/CmuxSettingsUITests/SettingsRowAnchorResolutionTests.swiftSources/App/ClaudeResumeAutoResponderController.swiftSources/CmuxSettingsJSONPathSupport.swiftSources/KeyboardShortcutSettingsFileStore+Template.swiftSources/KeyboardShortcutSettingsFileStore.swiftSources/SettingsNavigation.swiftSources/Workspace.swiftweb/data/cmux.schema.json
Greptile SummaryAdds
Confidence Score: 4/5Safe to merge; the default The detection and keystroke-planning logic is pure and well-tested. The one area to revisit is the new Sources/App/ClaudeResumeAutoResponderController.swift — the runtime singleton; Resources/Localizable.xcstrings — 18 locales still missing translations for the five new keys. Important Files Changed
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
%%{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
Reviews (12): Last reviewed commit: "Merge remote-tracking branch 'origin/mai..." | Re-trigger Greptile |
There was a problem hiding this comment.
Actionable comments posted: 4
♻️ Duplicate comments (2)
web/data/cmux.schema.json (1)
423-428:⚠️ Potential issue | 🟠 Major | ⚡ Quick winAdd
descriptionKeyforterminal.claudeResumeModeschema 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/**/*.jsonuser-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 winLog non-string
terminal.claudeResumeModevalues 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
⛔ Files ignored due to path filters (1)
.github/swift-file-length-budget.tsvis excluded by!**/*.tsv
📒 Files selected for processing (14)
Packages/macOS/CmuxSettings/Sources/CmuxSettings/Keys/TerminalCatalogSection.swiftPackages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ClaudeResumeMode.swiftPackages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ClaudeResumePrompt.swiftPackages/macOS/CmuxSettings/Tests/CmuxSettingsTests/ClaudeResumeModeTests.swiftPackages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Navigation/CuratedSettingEntry+Default.swiftPackages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/TerminalSection.swiftPackages/macOS/CmuxSettingsUI/Tests/CmuxSettingsUITests/SettingsRowAnchorResolutionTests.swiftSources/App/ClaudeResumeAutoResponderController.swiftSources/CmuxSettingsJSONPathSupport.swiftSources/KeyboardShortcutSettingsFileStore+Template.swiftSources/KeyboardShortcutSettingsFileStore.swiftSources/SettingsNavigation.swiftSources/Workspace.swiftweb/data/cmux.schema.json
|
Thanks for the reviews — all findings addressed in CodeRabbit
Greptile
Also wired Verified locally on Xcode 16.4 / Swift 6.1.2: |
2303e53 to
ffa9295
Compare
There was a problem hiding this comment.
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 winUpdate 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
⛔ Files ignored due to path filters (1)
.github/swift-file-length-budget.tsvis excluded by!**/*.tsv
📒 Files selected for processing (8)
Packages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ClaudeResumeMode.swiftPackages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ClaudeResumePrompt.swiftPackages/macOS/CmuxSettings/Tests/CmuxSettingsTests/ClaudeResumeModeTests.swiftPackages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Navigation/CuratedSettingEntry+Default.swiftSources/App/ClaudeResumeAutoResponderController.swiftSources/KeyboardShortcutSettingsFileStore.swiftSources/Workspace.swiftcmux.xcodeproj/project.pbxproj
5d38344 to
e951c80
Compare
There was a problem hiding this comment.
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
⛔ Files ignored due to path filters (1)
.github/swift-file-length-budget.tsvis excluded by!**/*.tsv
📒 Files selected for processing (15)
Packages/macOS/CmuxSettings/Sources/CmuxSettings/Keys/TerminalCatalogSection.swiftPackages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ClaudeResumeMode.swiftPackages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ClaudeResumePrompt.swiftPackages/macOS/CmuxSettings/Tests/CmuxSettingsTests/ClaudeResumeModeTests.swiftPackages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Navigation/CuratedSettingEntry+Default.swiftPackages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/TerminalSection.swiftPackages/macOS/CmuxSettingsUI/Tests/CmuxSettingsUITests/SettingsRowAnchorResolutionTests.swiftSources/App/ClaudeResumeAutoResponderController.swiftSources/CmuxSettingsJSONPathSupport.swiftSources/KeyboardShortcutSettingsFileStore+Template.swiftSources/KeyboardShortcutSettingsFileStore.swiftSources/SettingsNavigation.swiftSources/Workspace.swiftcmux.xcodeproj/project.pbxprojweb/data/cmux.schema.json
|
Follow-up — addressed the remaining open threads in
All mirrors the merged |
… 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>
1c69a37 to
ab0f531
Compare
# Conflicts: # .github/swift-file-length-budget.tsv
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
Packages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ClaudeResumePrompt.swift (1)
35-36: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winRequire 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 singleoptionLinesrow. In that case.full/.summaryreturns[.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
⛔ Files ignored due to path filters (1)
.github/swift-file-length-budget.tsvis excluded by!**/*.tsv
📒 Files selected for processing (15)
Packages/macOS/CmuxSettings/Sources/CmuxSettings/Keys/TerminalCatalogSection.swiftPackages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ClaudeResumeMode.swiftPackages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ClaudeResumePrompt.swiftPackages/macOS/CmuxSettings/Tests/CmuxSettingsTests/ClaudeResumeModeTests.swiftPackages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Navigation/CuratedSettingEntry+Default.swiftPackages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/TerminalSection.swiftPackages/macOS/CmuxSettingsUI/Tests/CmuxSettingsUITests/SettingsRowAnchorResolutionTests.swiftResources/Localizable.xcstringsSources/App/ClaudeResumeAutoResponderController.swiftSources/CmuxSettingsJSONPathSupport.swiftSources/KeyboardShortcutSettingsFileStore+Template.swiftSources/KeyboardShortcutSettingsFileStore.swiftSources/SettingsNavigation.swiftSources/Workspace.swiftcmux.xcodeproj/project.pbxproj
💤 Files with no reviewable changes (2)
- cmux.xcodeproj/project.pbxproj
- Sources/Workspace.swift
# Conflicts: # .github/swift-file-length-budget.tsv
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Summary
What? Adds a
terminal.claudeResumeModesetting (ask|full|summary, defaultask) 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: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:
Workspace.createPanelwhen a Claude agent is being auto-resumed (restorableAgent.kind == .claudeand a resume command/input will run) and the mode isn't.ask.ClaudeResumeAutoResponderController, modeled onAgentHibernationController) reads the pane's rendered text via the existingTerminalSurface.readTextand, when the menu appears, sends the keys via the existingTerminalSurface.sendNamedKey(so libghostty encodes the right sequence for the pane's cursor mode).Design
ClaudeResumeMode(SettingCodableenum, mirrorsWorkspaceGroupNewPlacement) andClaudeResumePrompt(detection + keystroke planning) live inCmuxSettingswith 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.createPanelare the only app-side glue.askmeans zero behavior change unless opted in.Wired through the usual surfaces: Settings → Terminal Picker (
CmuxSettingsUI), settings search index + curated entry,cmux.jsonschema + template + JSON-path support, and the file-store parser.Testing
CmuxSettingsTests/ClaudeResumeModeTests.swiftcovering: 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 inask).Checklist
CmuxSettingsTests)ask) preserves existing behaviorNeed help on this PR? Tag
/codesmithwith 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 isask; the responder is one‑shot, retries until keys are accepted, and ignores stale menu text.New Features
terminal.claudeResumeMode; searchable and localized; added to schema (withdescriptionKey), template, JSON‑path, and file‑store parsing. Strict enum parsing (ask/full/summaryonly); non‑string or invalid values are logged.Workspace.createPanel.Migration
ask.terminal.claudeResumeModetofullorsummaryin Settings → Terminal or incmux.json.Written for commit 9a8ed08. Summary will update on new commits.
Summary by CodeRabbit
terminal.claudeResumeMode, and included in the default template and configuration schema.