Skip to content

Open terminal file links in editor - #3977

Closed
austinywang wants to merge 19 commits into
mainfrom
issue-1544-cmd-click-file-to-ide
Closed

austinywang wants to merge 19 commits into
mainfrom
issue-1544-cmd-click-file-to-ide

Conversation

@austinywang

@austinywang austinywang commented May 12, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • route terminal file references through the preferred editor instead of browser routing
  • preserve file:line and file:line:column metadata for editor commands
  • add regression coverage for relative and absolute terminal file link resolution

Closes #1544

Testing

  • Not run locally per repository testing policy.

Note

Medium Risk
Touches terminal link classification, async open-url handling, and editor invocation paths where misclassification could open wrong targets or skip in-app routing; scope is large but covered by new tests.

Overview
Terminal file links (including path:line[:column], relative paths, extensionless names like Makefile, and file: URLs with #L… fragments) are resolved against the terminal CWD and opened through the preferred editor, with line/column passed via URL fragments and editor-specific flags (-g / path:line:col for VS Code–family commands). HTTP(S) and odd scheme:port tokens stay on the browser/external path; unverified absolute paths are not opened on remote terminals.

In-app routing gains an explicit CommandClickFileRouteKind, off-main route checks in deferred opens, and a markdown → file-preview fallback when a markdown split cannot be created. Ghostty open_url handling is refactored to resolve off the main thread and apply opens on the main actor, using PreferredEditorSettings (and cmux splits only for fragment-free local files when routing applies).

Shared CmuxShellWords replaces ad-hoc shell splitting for agent resume and editor command parsing.

Reviewed by Cursor Bugbot for commit a80502d. Bugbot is set up for automated code reviews on this repo. Configure here.


Summary by cubic

Open terminal file links in the preferred editor with correct line/column jumps, while keeping web URLs in the browser and routing Markdown/supported files in‑app. Resolution runs off‑main with a clear action context, preserves file: fragments, and resolves relative paths against the terminal CWD (closes #1544).

  • New Features

    • Prefer local file refs (path:line[:col], file:) over browser URLs; resolve against terminal CWD; support extensionless files (e.g., Makefile); emit file://…#L….
    • Open via PreferredEditorSettings; parse #L… fragments; add --goto/-g for code/cursor/windsurf; shared CmuxShellWords; explicit CommandClickFileRouteKind with markdown→preview fallback.
  • Bug Fixes

    • More accurate classification: keep http/https and scheme:port tokens (e.g., config:8080) external; avoid host:port false positives.
    • Safer opening: preserve file: line fragments; don't route file#L… into in‑app preview; resolve off‑main and defer UI on main; preserve source workspace/panel; skip absolute‑path fallbacks for remote terminals; fallback to NSWorkspace when editor routing fails.

Written for commit a80502d. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features

    • Smarter editor launches: preserves line/column, adapts invocation flags, and supports shell-aware tokenization.
    • Enhanced terminal link handling with richer local-file parsing, cwd support, and host/fragment normalization.
  • Bug Fixes

    • More reliable terminal link routing and safer deferred opening with validated fallbacks.
    • Improved handling for command-click file routing with proper fallback to existing previews.
  • Tests

    • Added unit and integration tests covering editor invocation, shell tokenization, and terminal URL/file resolution.

@vercel

vercel Bot commented May 12, 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 4, 2026 1:43pm
cmux-staging Building Building Preview, Comment Jun 4, 2026 1:43pm

@coderabbitai

coderabbitai Bot commented May 12, 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

Centralizes terminal file/path opening: shell-aware tokenization, preferred-editor open with fragment parsing, a route-kind router for command-clicks, terminal-local file resolution with cwd/fileExists injection, and an async open_url pipeline that defers CMUX opens with revalidation.

Changes

File path opening from terminal interactions

Layer / File(s) Summary
Shell word tokenization primitive
Sources/cmuxApp.swift, Sources/RestorableAgentSession.swift
CmuxShellWords.split(_:) tokenizes command strings while respecting quotes and backslash escaping; used by preferred editor parsing and agent resume command building.
Preferred editor file opening with line/column support
Sources/cmuxApp.swift, cmuxTests/PreferredEditorSettingsTests.swift
OpenTarget captures path and optional line/column; PreferredEditorSettings.open(_:) parses fragments, records UI-test capture, computes editor-specific goto flags using tokenized commands, builds a shell-quoted /bin/sh -c invocation, and falls back to fallbackURL on failure. Tests validate fragment preservation and tokenization.
Command-click file routing centralization
Sources/CommandClickFileOpenRouter.swift, cmuxTests/WindowAndDragTests.swift
CommandClickFileRouteKind and routeKind(path:) centralize routing between markdown and supported files; openInCmux resolves route kind up-front and dispatches; deferredOpenFileInCmux uses a main-actor Task with off-main routeKind computation, revalidation, and optional fallback.
Terminal-local file reference resolution
Sources/GhosttyTerminalView.swift, cmuxTests/TerminalAndGhosttyTests.swift
TerminalLocalFileReference parses paths with optional :line:column; resolveTerminalOpenURLTarget gains cwd, allowAbsolutePathFallback, and fileExists parameters, prioritizes terminal-local and file:// handling, preserves fragments when producing local file:// URLs, and is exercised by new resolution tests.
Terminal open_url action routing pipeline
Sources/GhosttyTerminalView.swift
GHOSTTY_ACTION_OPEN_URL now validates input, resolves targets off-main in a detached task, then on the main actor routes local-file targets into CMUX when eligible (deferred), or opens externally/embedded-browser with preflight and fallback behavior.
Test coverage for file resolution and editor opening
cmuxTests/PreferredEditorSettingsTests.swift, cmuxTests/TerminalAndGhosttyTests.swift, cmuxTests/WindowAndDragTests.swift
New tests cover shell-word splitting, fallback URL fragment preservation, file:// resolution preserving #L... fragments, editor-style path:line resolution to external file URLs, and command-click fallback to existing preview panels.

Sequence Diagrams

sequenceDiagram
    participant User
    participant Terminal
    participant Resolver
    participant Router
    participant Workspace

    User->>Terminal: Cmd+click file path
    Terminal->>Resolver: resolveTerminalOpenURLTarget(raw, cwd, fileExists)
    Resolver-->>Terminal: TerminalOpenURLTarget (local file)
    Terminal->>Router: deferredOpenFileInCmux(workspace, panelId, path)
    Router->>Router: Task.detached -> compute routeKind(path)
    Router->>Workspace: openOrFocus*Split based on routeKind
    Workspace-->>Router: success/failure
    Router-->>Terminal: opened (or fallback invoked)
Loading
sequenceDiagram
    participant Terminal
    participant ActionHandler
    participant Resolver
    participant EditorSettings

    Terminal->>ActionHandler: GHOSTTY_ACTION_OPEN_URL(urlString)
    ActionHandler->>Resolver: resolve off-main (cwd, fileExists)
    Resolver-->>ActionHandler: TerminalOpenURLTarget
    ActionHandler->>EditorSettings: open(resolvedURL) or deferred CMUX open
    EditorSettings->>EditorSettings: parse fragment and tokenize command
    EditorSettings->>EditorSettings: build shell command and exec
    EditorSettings-->>ActionHandler: process result (success/fallback)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

  • manaflow-ai/cmux#4594: Modifies CommandClickFileOpenRouter and Ghostty routing paths used by this PR.
  • manaflow-ai/cmux#4864: Overlaps on cmd-click markdown routing and terminal open_url resolution changes.
  • manaflow-ai/cmux#5250: Integrates config-file openers with PreferredEditorSettings, which this PR refactors.

"I nibble lines and hop in cheer,
Paths and fragments now appear,
Cmd+click leads me straight to sight,
Editors open, bits feel right,
A rabbit's clap — code takes flight!"


Caution

Pre-merge checks failed

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

  • Ignore

❌ Failed checks (3 errors, 1 warning)

Check name Status Explanation Resolution
Cmux Swift Concurrency ❌ Error cmuxApp.swift introduces DispatchQueue.global().async with blocking process.waitUntilExit(); codebase uses Process.terminationHandler elsewhere. Replace DispatchQueue.global().async with Process.terminationHandler callback for async process exit handling.
Cmux Swift File And Package Boundaries ❌ Error GhosttyTerminalView.swift already exceeds budget (13,458→15,841 lines) and PR adds 421+ lines without extraction, violating the >250-line addition threshold for oversized files. Extract parsing/subprocess logic (CmuxShellWords, PreferredEditorSettings, TerminalURLResolution) into small CmuxShellParsing or CmuxEditorInvocation package to shrink GhosttyTerminalView by 200+ lines and enable editor-invocation reuse.
Cmux Swift Logging ❌ Error Unguarded NSLog in Sources/cmuxApp.swift's terminateForMissingLaunchTag() violates swift-logging.md rule against NSLog in app/runtime code. Guard NSLog with #if DEBUG or use Logger from os.log instead of NSLog for production diagnostics.
Docstring Coverage ⚠️ Warning Docstring coverage is 4.41% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (14 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Open terminal file links in editor' directly describes the main change in the changeset, which refactors terminal URL handling to route file paths through the preferred editor instead of the browser.
Linked Issues check ✅ Passed The code changes comprehensively address all requirements from issue #1544: detects local file paths vs URLs, resolves relative paths against CWD, opens files in configured editor, preserves line/column metadata, validates file existence, and supports editor-specific flags.
Out of Scope Changes check ✅ Passed All changes are tightly scoped to the objectives: terminal file link routing, editor integration, shell word parsing for editor commands/agent resume, CmuxShellWords introduction, CommandClickFileRouteKind, and corresponding test coverage. No unrelated changes detected.
Cmux Swift Actor Isolation ✅ Passed Production changes follow Swift 6 actor isolation: Sendable types marked, MainActor methods annotated, background work properly isolated, no shared mutable state violations found.
Cmux Swift Blocking Runtime ✅ Passed PR uses Task.detached for off-main resolution and Task { @MainActor } for deferred opens; process.waitUntilExit correctly executes on background thread, not main thread.
Cmux No Hacky Sleeps ✅ Passed All modified files are Swift code, out of scope per rule. Rule applies only to TypeScript, JavaScript, shell, and non-Swift runtime scripts; Swift timing covered separately.
Cmux Algorithmic Complexity ✅ Passed Tiny fixed-size collections (4-6 items) with linear algorithms; terminal URL resolution called once per click; no hot-path violations found.
Cmux Swift @Concurrent ✅ Passed No violations: no nonisolated async without @concurrent, no @concurrent on sync/isolated functions, and file I/O work properly hopped off MainActor via Task.detached.
Cmux User-Facing Error Privacy ✅ Passed No user-facing errors violate privacy rules: no vendor names, credentials, env vars, raw messages, or sensitive details exposed in error handling or UI feedback.
Cmux Full Internationalization ✅ Passed PR introduces no new user-facing text requiring localization: new code is internal routing logic, test-only, or reuses existing localized patterns already in String(localized:) calls.
Cmux Swiftui State Layout ✅ Passed No SwiftUI state violations. PR adds no new ObservableObject/@published classes, GeometryReader, or render-time mutations. Existing state untouched; Task/DispatchQueue in non-render contexts.
Cmux Architecture Rethink ✅ Passed Deferred Task pattern is documented platform bridge for deadlock avoidance (#3370); background dispatch for process.waitUntilExit is required; no mutable state duplication or split ownership.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR introduces routing logic and editor process launching without creating new standalone cmux windows or NSWindowController classes requiring registration.
Description check ✅ Passed The PR description covers all required sections: summary of changes, testing approach, and includes checklists with key tasks marked.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-1544-cmd-click-file-to-ide

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

❤️ Share

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

@greptile-apps

greptile-apps Bot commented May 12, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR routes terminal file references (path:line[:column], relative and absolute paths, extensionless files like Makefile, and file: URLs with #L… fragments) through PreferredEditorSettings instead of the browser, adding --goto/-g flags for VS Code–family editors and resolving relative paths against the terminal CWD. The Ghostty open_url callback now returns immediately after enqueuing an async main-actor task, with file-existence probes moved to a detached task.

  • New resolveTerminalOpenURLTarget with cwd/allowAbsolutePathFallback/fileExists parameters implements path:line:column parsing, file: URL resolution, fragment preservation, and extensionless-filename recognition; remote terminals opt out via allowAbsolutePathFallback: false.
  • Shared CmuxShellWords replaces the private splitShellWords in RestorableAgentSession and drives commandNeedsGotoFlag; behavior for backslash-in-single-quotes and empty quoted strings differs subtly from the old implementation.
  • CommandClickFileOpenRouter.deferredOpenFileInCmux migrated from DispatchQueue.main.async to Task { @MainActor } with an off-main routeKind check, and explicit CommandClickFileRouteKind now drives markdown-then-preview fallback routing.

Confidence Score: 4/5

Safe to merge; the async dispatch chain correctly moves file I/O off the main actor and all previously identified blocking-path concerns are resolved.

The async three-phase design is sound and all previously flagged synchronous-main-thread issues are resolved. The only delta from clean is a concurrency-style choice (Task.detached wrapping a pure synchronous routeKind call) and a behavioral note about CmuxShellWords semantics vs the old splitShellWords. Neither affects runtime correctness for any realistic input.

No files require special attention; the most active changes in GhosttyTerminalView.swift and cmuxApp.swift both read cleanly.

Important Files Changed

Filename Overview
Sources/GhosttyTerminalView.swift Core change: async OPEN_URL callback split into main-actor context capture, detached resolution, and main-actor UI application. Adds path:line:column parsing, file:// fragment preservation, and extensionless-filename matching. Previously flagged blocking-path issues are resolved.
Sources/cmuxApp.swift Adds CmuxShellWords tokenizer, OpenTarget struct, and commandInvocationComponents. Tokenizer has two subtle behavioral differences from the old splitShellWords: POSIX-correct single-quote backslash handling and empty-quoted-string token emission.
Sources/CommandClickFileOpenRouter.swift Refactored to use explicit CommandClickFileRouteKind; deferredOpenFileInCmux migrated from DispatchQueue.main.async to Task { @mainactor }. Markdown fallback to file-preview on split failure correctly implemented.
Sources/RestorableAgentSession.swift splitShellWords now delegates to CmuxShellWords.split. Minor behavioral difference for empty quoted strings; unlikely to affect agent resume commands in practice.
cmuxTests/TerminalAndGhosttyTests.swift Good regression coverage for fragment preservation, relative file:// CWD resolution, extensionless filenames, http://host:port guard, config:8080 guard, and allowAbsolutePathFallback=false.
cmuxTests/PreferredEditorSettingsTests.swift New tests cover fallback URL fragment preservation, --goto injection for known editors, no-op for unknown editors, and quoted editor-path shell-word detection.
cmuxTests/WindowAndDragTests.swift Added test for markdown-route falling back to existing file-preview panel. Clean addition.

Sequence Diagram

sequenceDiagram
    participant Ghostty as Ghostty Callback
    participant MA as MainActor
    participant DT as Detached Task
    participant FS as FileManager
    participant Editor as PreferredEditorSettings
    participant CMux as deferredOpenFileInCmux

    Ghostty->>MA: GHOSTTY_ACTION_OPEN_URL (urlString)
    MA->>MA: capture TerminalOpenURLActionContext
    MA-->>Ghostty: return true
    MA->>DT: Task.detached resolveOpenURLActionTarget(context)
    DT->>FS: fileExists(path)
    FS-->>DT: Bool
    DT->>DT: resolveTerminalOpenURLTarget
    DT->>MA: MainActor.run handleResolvedOpenURLTarget
    alt local file, no fragment
        MA->>CMux: deferredOpenFileInCmux
        CMux->>MA: openInCmux or PreferredEditorSettings.open
    else local file with fragment
        MA->>Editor: PreferredEditorSettings.open (line/col from fragment)
    else embeddedBrowser
        MA->>MA: openEmbeddedBrowserLink
    else external
        MA->>MA: NSWorkspace.shared.open
    end
Loading

Reviews (12): Last reviewed commit: "fix: preserve line jump editor routing" | Re-trigger Greptile

Comment thread Sources/GhosttyTerminalView.swift
Comment thread Sources/cmuxApp.swift Outdated
Comment thread Sources/GhosttyTerminalView.swift Outdated
Comment thread Sources/GhosttyTerminalView.swift Outdated
Comment thread Sources/GhosttyTerminalView.swift
Comment thread Sources/GhosttyTerminalView.swift Outdated
Comment thread Sources/cmuxApp.swift Outdated
Comment thread Sources/GhosttyTerminalView.swift
Comment thread Sources/cmuxApp.swift Outdated
…file-to-ide

# Conflicts:
#	Sources/GhosttyTerminalView.swift
Comment thread Sources/GhosttyTerminalView.swift Outdated
Comment thread Sources/cmuxApp.swift Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

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

Inline comments:
In `@cmuxTests/TerminalAndGhosttyTests.swift`:
- Around line 5142-5146: The test currently mutates the process-global CWD using
FileManager.default.changeCurrentDirectoryPath (saving originalDirectory and
deferring restore), which risks cross-test flakiness; instead, stop changing the
global CWD and call resolveTerminalOpenURLTarget with an explicit cwd parameter
pointing to root.path (e.g. replace the changeCurrentDirectoryPath/defer block
and pass cwd: root.path into resolveTerminalOpenURLTarget when resolving
"Sources/App.swift:1"). Ensure any reference to originalDirectory and the
deferred restore are removed and the test still unwraps the returned target via
try XCTUnwrap.
🪄 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: 025751f3-bcbf-403d-9f3f-6baf7564fbb1

📥 Commits

Reviewing files that changed from the base of the PR and between d02e6d6 and eb04850.

📒 Files selected for processing (6)
  • Sources/CommandClickFileOpenRouter.swift
  • Sources/GhosttyTerminalView.swift
  • Sources/RestorableAgentSession.swift
  • Sources/cmuxApp.swift
  • cmuxTests/PreferredEditorSettingsTests.swift
  • cmuxTests/TerminalAndGhosttyTests.swift
👮 Files not reviewed due to content moderation or server errors (1)
  • Sources/GhosttyTerminalView.swift

Comment thread cmuxTests/TerminalAndGhosttyTests.swift Outdated
Comment thread Sources/GhosttyTerminalView.swift
Comment thread Sources/CommandClickFileOpenRouter.swift Outdated
Comment thread Sources/GhosttyTerminalView.swift

@cubic-dev-ai cubic-dev-ai 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.

1 issue found across 3 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread Sources/GhosttyTerminalView.swift Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

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

Inline comments:
In `@Sources/CommandClickFileOpenRouter.swift`:
- Around line 100-107: The code currently falls back to the captured workspace
reference after an async hop (using
AppDelegate.shared?.workspaceContainingPanel(... )?.workspace ?? workspace)
which can be stale; change this to re-resolve the live Workspace instance by
ID/ownership (use AppDelegate.shared?.workspaceContainingPanel(panelId:
surfaceId, preferredWorkspaceId: preferredWorkspaceId)?.workspace and if that
lookup returns nil call the provided fallback closure instead of using the
captured workspace variable), ensuring you do not mutate or reuse the captured
workspace; reference the symbols workspaceContainingPanel, AppDelegate.shared,
preferredWorkspaceId, surfaceId and fallback when making the fix.
🪄 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: 9406002e-feda-4241-8c08-14cb62b54710

📥 Commits

Reviewing files that changed from the base of the PR and between eb04850 and 6a7e6ae.

📒 Files selected for processing (3)
  • Sources/CommandClickFileOpenRouter.swift
  • Sources/GhosttyTerminalView.swift
  • cmuxTests/TerminalAndGhosttyTests.swift
👮 Files not reviewed due to content moderation or server errors (1)
  • Sources/GhosttyTerminalView.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.

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 1

🤖 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/CommandClickFileOpenRouter.swift`:
- Around line 100-107: The code currently falls back to the captured workspace
reference after an async hop (using
AppDelegate.shared?.workspaceContainingPanel(... )?.workspace ?? workspace)
which can be stale; change this to re-resolve the live Workspace instance by
ID/ownership (use AppDelegate.shared?.workspaceContainingPanel(panelId:
surfaceId, preferredWorkspaceId: preferredWorkspaceId)?.workspace and if that
lookup returns nil call the provided fallback closure instead of using the
captured workspace variable), ensuring you do not mutate or reuse the captured
workspace; reference the symbols workspaceContainingPanel, AppDelegate.shared,
preferredWorkspaceId, surfaceId and fallback when making the fix.
🪄 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: 9406002e-feda-4241-8c08-14cb62b54710

📥 Commits

Reviewing files that changed from the base of the PR and between eb04850 and 6a7e6ae.

📒 Files selected for processing (3)
  • Sources/CommandClickFileOpenRouter.swift
  • Sources/GhosttyTerminalView.swift
  • cmuxTests/TerminalAndGhosttyTests.swift
👮 Files not reviewed due to content moderation or server errors (1)
  • Sources/GhosttyTerminalView.swift
🛑 Comments failed to post (1)
Sources/CommandClickFileOpenRouter.swift (1)

100-107: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don’t fall back to the captured workspace after the async hop.

On Lines 104-107, workspaceContainingPanel(... )?.workspace ?? workspace can reopen on a stale Workspace instance if the panel moved or the tab list was rebuilt before this task runs. That makes the deferred open target the wrong workspace or a detached object. Re-resolve the live workspace by ID/ownership here and call fallback when that lookup fails instead of mutating the captured fallback object.

Based on learnings, workspace mutations must re-resolve the live Workspace instance because restoreSessionSnapshot() can replace tabs with fresh instances, making captured references stale.

🤖 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/CommandClickFileOpenRouter.swift` around lines 100 - 107, The code
currently falls back to the captured workspace reference after an async hop
(using AppDelegate.shared?.workspaceContainingPanel(... )?.workspace ??
workspace) which can be stale; change this to re-resolve the live Workspace
instance by ID/ownership (use
AppDelegate.shared?.workspaceContainingPanel(panelId: surfaceId,
preferredWorkspaceId: preferredWorkspaceId)?.workspace and if that lookup
returns nil call the provided fallback closure instead of using the captured
workspace variable), ensuring you do not mutate or reuse the captured workspace;
reference the symbols workspaceContainingPanel, AppDelegate.shared,
preferredWorkspaceId, surfaceId and fallback when making the fix.

Comment thread Sources/GhosttyTerminalView.swift
coderabbitai[bot]
coderabbitai Bot previously requested changes Jun 4, 2026

@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 `@cmuxTests/TerminalAndGhosttyTests.swift`:
- Around line 5200-5206: The test currently allows both .external and
.embeddedBrowser for the evaluated target, which masks regressions; change the
assertion to require the concrete .external case for the evaluated target
variable (use a guard/case let .external(url) else { XCTFail(...); return } or
an XCTAssertTrue case comparison), then assert url.isFileURL is false and
url.absoluteString == "config:8080" to ensure only the expected external route
is accepted.
- Around line 5183-5185: The test is asserting the full URL string which can
fail due to harmless normalization; instead in the .embeddedBrowser(url) branch
inspect URL components: assert url.scheme == "http", url.host == "localhost",
and assert url.port == 3000 (or if port is nil assert default 80 is not
expected), and optionally assert that url.path is "/" or startsWith the expected
path rather than using url.absoluteString; update the
XCTAssertEqual(url.absoluteString, ...) to these component checks in the
.embeddedBrowser(url) case and leave the .external(url) case unchanged.
🪄 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: 40ddfc37-1fed-4082-ae72-aa4b322941f7

📥 Commits

Reviewing files that changed from the base of the PR and between 6a7e6ae and 935a7d2.

📒 Files selected for processing (2)
  • Sources/GhosttyTerminalView.swift
  • cmuxTests/TerminalAndGhosttyTests.swift
👮 Files not reviewed due to content moderation or server errors (1)
  • Sources/GhosttyTerminalView.swift

Comment thread cmuxTests/TerminalAndGhosttyTests.swift
Comment thread cmuxTests/TerminalAndGhosttyTests.swift

@cubic-dev-ai cubic-dev-ai 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.

3 issues found across 6 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread Sources/cmuxApp.swift Outdated
Comment thread Sources/CommandClickFileOpenRouter.swift
Comment thread Sources/GhosttyTerminalView.swift

@cursor cursor 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.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 22509e3. Configure here.

Comment thread Sources/GhosttyTerminalView.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.

Caution

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

⚠️ Outside diff range comments (2)
Sources/cmuxApp.swift (2)

5254-5475: 🛠️ Refactor suggestion | 🟠 Major | 🏗️ Heavy lift

Extract shell/editor routing logic out of cmuxApp.swift.

CmuxShellWords + PreferredEditorSettings are core, testable logic that do not depend on SwiftUI app lifecycle, but they’re being expanded in an already oversized root app file. Move them into a focused module/file (and package target if reused) to keep boundaries and ownership clear.

As per coding guidelines: “Flag features implemented directly in the app target/module's root Sources path when core logic is independent of cmux app lifecycle...” and “Flag Swift production files that exceed 400 lines without a clear single responsibility...”.

🤖 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/cmuxApp.swift` around lines 5254 - 5475, Extract CmuxShellWords and
PreferredEditorSettings (including their helper types and functions like
OpenTarget, commandInvocationComponents, lineColumn, commandNeedsGotoFlag,
normalizedCommandName, firstShellWord, shellQuote) into a new focused Swift
source file and, if reused by other targets, into a new module/package target;
update visibility (make types/functions internal/public as needed for tests),
add necessary imports (Foundation, AppKit) at the top of the new file, remove
the duplicated definitions from cmuxApp.swift, and update any unit tests or
references to import the new module or file so existing callers (e.g.,
PreferredEditorSettings.open and PreferredEditorSettings.resolvedCommand)
continue to work.

5367-5372: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Avoid blocking a global worker thread per editor launch.

process.waitUntilExit() inside DispatchQueue.global().async can strand a worker thread for the full editor lifetime (e.g., vim), and repeated cmd-click opens can accumulate blocked threads. Prefer process.terminationHandler to observe exit without blocking a pooled thread.

Suggested change
         do {
             try process.run()
-            // Check exit status on a background thread; fall back on failure
-            // (e.g. command not found exits 127 but /bin/sh itself succeeds)
-            DispatchQueue.global(qos: .userInitiated).async {
-                process.waitUntilExit()
-                if process.terminationStatus != 0 {
-                    DispatchQueue.main.async { NSWorkspace.shared.open(target.fallbackURL) }
-                }
-            }
+            process.terminationHandler = { proc in
+                guard proc.terminationStatus != 0 else { return }
+                DispatchQueue.main.async {
+                    NSWorkspace.shared.open(target.fallbackURL)
+                }
+            }
         } catch {
             NSWorkspace.shared.open(target.fallbackURL)
         }
🤖 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/cmuxApp.swift` around lines 5367 - 5372, The current code spawns a
pooled thread with DispatchQueue.global(qos: .userInitiated).async and calls
process.waitUntilExit(), which can block a worker thread for the lifetime of the
editor; replace this pattern by assigning process.terminationHandler to a
closure that checks process.terminationStatus and, if non‑zero, calls
DispatchQueue.main.async { NSWorkspace.shared.open(target.fallbackURL) } so no
global worker thread is blocked. Locate the block using process.waitUntilExit(),
DispatchQueue.global(...).async and NSWorkspace.shared.open(target.fallbackURL)
and convert it to use process.terminationHandler instead.
🤖 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.

Outside diff comments:
In `@Sources/cmuxApp.swift`:
- Around line 5254-5475: Extract CmuxShellWords and PreferredEditorSettings
(including their helper types and functions like OpenTarget,
commandInvocationComponents, lineColumn, commandNeedsGotoFlag,
normalizedCommandName, firstShellWord, shellQuote) into a new focused Swift
source file and, if reused by other targets, into a new module/package target;
update visibility (make types/functions internal/public as needed for tests),
add necessary imports (Foundation, AppKit) at the top of the new file, remove
the duplicated definitions from cmuxApp.swift, and update any unit tests or
references to import the new module or file so existing callers (e.g.,
PreferredEditorSettings.open and PreferredEditorSettings.resolvedCommand)
continue to work.
- Around line 5367-5372: The current code spawns a pooled thread with
DispatchQueue.global(qos: .userInitiated).async and calls
process.waitUntilExit(), which can block a worker thread for the lifetime of the
editor; replace this pattern by assigning process.terminationHandler to a
closure that checks process.terminationStatus and, if non‑zero, calls
DispatchQueue.main.async { NSWorkspace.shared.open(target.fallbackURL) } so no
global worker thread is blocked. Locate the block using process.waitUntilExit(),
DispatchQueue.global(...).async and NSWorkspace.shared.open(target.fallbackURL)
and convert it to use process.terminationHandler instead.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: a765e50c-2b83-4cad-85ea-694142457e84

📥 Commits

Reviewing files that changed from the base of the PR and between bb5e389 and a80502d.

📒 Files selected for processing (6)
  • Sources/CommandClickFileOpenRouter.swift
  • Sources/GhosttyTerminalView.swift
  • Sources/cmuxApp.swift
  • cmuxTests/PreferredEditorSettingsTests.swift
  • cmuxTests/TerminalAndGhosttyTests.swift
  • cmuxTests/WindowAndDragTests.swift
👮 Files not reviewed due to content moderation or server errors (1)
  • Sources/GhosttyTerminalView.swift

@austinywang
austinywang dismissed coderabbitai[bot]’s stale review June 4, 2026 13:53

Stale CodeRabbit review on an older commit; both actionable threads were fixed in follow-up commits, all review threads are resolved, and CodeRabbit re-reviewed the latest head without requesting changes.

Eridanus117 added a commit to Eridanus117/cmux that referenced this pull request Jul 1, 2026
Resolve terminal file links of the form `path:line[:column]` — the
convention Claude Code and Codex print for click-to-open — to the
referenced file and open it at that line in the configured editor:

- `String.splitTerminalPathLineSuffix()` peels a trailing `:line[:column]`.
- `TerminalPathResolver.resolveOpenURLFileReference(_:cwd:)` resolves a token
  to an existing file plus optional line/column, probing the literal path
  first (so a file that really ends in a colon-number wins) and the
  line-stripped path second; web URLs are never treated as file paths.
- `TerminalFileReference.fileURL` carries line/column across the package
  boundary as a `#L<line>[:<column>]` fragment.
- `PreferredEditorService.open` decodes that fragment and hands
  `path:line:column` (plus `-g`) to VS Code-family editors, falling back to
  the fragment-free file URL for other editors and the system opener.
- The terminal open-URL handler routes any line reference to the editor,
  which also fixes relative `dir/file.swift:line` links that previously fell
  through to the browser omnibar.

Verified with package-level swift test on CmuxTerminalCore and CmuxWorkspaces
(red without the fix, green with it). No new user-facing strings.

Supersedes manaflow-ai#3977 by @austinywang, which implemented the same user-facing
behavior before manaflow-ai#5894 extracted the resolver into CmuxTerminalCore /
CmuxWorkspaces; the `:line` split and editor `-g` dispatch are adapted from
that PR onto the new package seams.

Co-authored-by: austinpower1258 <austinwang115@gmail.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@lawrencecchen lawrencecchen added the stale-revisit Closed after 30+ days without activity; preserved for possible revisit or reopening. label Sep 23, 2026
@github-project-automation github-project-automation Bot moved this from Todo to Done in cmux backlog Sep 23, 2026

This branch was successfully deployed

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

Labels

stale-revisit Closed after 30+ days without activity; preserved for possible revisit or reopening.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Cmd+click on file paths should open in IDE instead of browser

3 participants