Skip to content

Add hidden CLI command for live terminal debugging - #1599

Merged
lawrencecchen merged 4 commits into
mainfrom
task-cli-ghost-terminal-debug
Mar 17, 2026
Merged

lawrencecchen merged 4 commits into
mainfrom
task-cli-ghost-terminal-debug

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented Mar 17, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • add a hidden cmux debug-terminals command backed by a debug.terminals socket method
  • enumerate every live Ghostty terminal surface, even when it is no longer represented in the cmux tree
  • print runtime, window, workspace, pane, surface, and visibility metadata that helps diagnose stray floating terminals after updates

Verification

  • ./scripts/reload.sh --tag task-cli-ghost-terminal-debug
  • cmux-dev --socket /tmp/cmux-debug-task-cli-ghost-terminal-debug.sock debug-terminals
  • verified the command reports an orphaned live surface with mapped=0 tree=0 in the tagged app

Summary by cubic

Add a hidden cmux debug-terminals command to inspect live Ghostty terminal surfaces across all windows/workspaces, including orphans, with readable or --json output. Tightens diagnostics with richer metadata, safer formatting, and better window/workspace/pane mapping.

  • New Features

    • Added debug.terminals socket method and hidden cmux debug-terminals command with human-readable output, --json, usage help, and argument validation.
    • Introduced TerminalSurfaceRegistry and per-surface debug metadata: created/runtime ages; teardown reason/age; initial command; portal host lease (id/inWindow/area).
    • Expanded diagnostics and mapping: window/workspace/pane indices and selection; window details (title/class/controller/delegate, number/key/visible/occluded/level/frame); hosted view state (visible/inWindow/superview/hidden/ancestorHidden, alpha, first responder, frame/bounds/frameInWindow, superview chain); terminal context, tty/cwd, git branch/dirty, listening ports; portal binding state/generation; stable ordering and fallback window resolution for orphans.
  • Refactors

    • Removed stray CLIProcessRunnerTests target and test files.

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

Summary by CodeRabbit

  • New Features

    • Added a CLI command "debug-terminals" to inspect all terminals, producing JSON (--json) or human-readable diagnostics.
  • Improvements

    • Enhanced terminal lifecycle tracking and added debug accessors plus a registry to surface richer runtime diagnostic metadata.
  • Tests

    • Removed the CLIProcessRunnerTests test suite.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@vercel

vercel Bot commented Mar 17, 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 Mar 17, 2026 10:55am

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@coderabbitai

coderabbitai Bot commented Mar 17, 2026 •

Copy link
Copy Markdown

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

Adds a CLI command "debug-terminals" that calls a new v2 method to enumerate TerminalSurface instances; introduces a thread-safe TerminalSurfaceRegistry, records surface lifecycle timestamps/teardown reasons, exposes debug accessors, and formats JSON or human-readable terminal-debug payloads (with DEBUG-gated test hooks).

Changes

Cohort / File(s) Summary
CLI Debug Command & Formatting
CLI/cmux.swift
Adds debug-terminals CLI path, invokes v2 method debug.terminals, implements payload formatting helpers (parsing/formatting metadata, rects, ports, ages) and DEBUG-only test hooks (debugUsageTextForTesting, debugFormatDebugTerminalsPayloadForTesting).
TerminalController v2
Sources/TerminalController.swift
Adds v2DebugTerminals(params:), advertises "debug.terminals" in v2 capabilities, and builds a detailed terminal-introspection payload by querying surfaces via registry and surface debug accessors.
Terminal Surface Tracking & Debug Accessors
Sources/GhosttyTerminalView.swift
Introduces TerminalSurfaceRegistry (singleton, thread-safe, returns surfaces sorted by id), registers surfaces on init, adds creation/runtime/teardown timestamps and teardown reason, adds recordRuntimeSurfaceCreation()/recordTeardownRequest(reason:), and exposes multiple debug accessors (createdAt, runtimeSurfaceCreatedAt, teardown info, workspace id, context label, initial command, portal host lease).
Tests & Project file changes
cmuxTests/CLIProcessRunnerTests.swift, GhosttyTabs.xcodeproj/project.pbxproj
Removes the cmuxTests/CLIProcessRunnerTests.swift test suite (large deletion). project.pbxproj change is indentation-only for a file reference. No new tests added here.

Sequence Diagram(s)

sequenceDiagram
    participant CLI as CLI User
    participant CMux as CLI/cmux.swift
    participant TermCtrl as TerminalController
    participant Registry as TerminalSurfaceRegistry
    participant Surface as TerminalSurface

    CLI->>CMux: run "debug-terminals"
    CMux->>TermCtrl: v2.call("debug.terminals", params)
    TermCtrl->>Registry: allSurfaces()
    Registry-->>TermCtrl: [TerminalSurface...]
    loop per surface
        TermCtrl->>Surface: call debug accessors (timestamps, state, pointers)
        Surface-->>TermCtrl: metadata
    end
    TermCtrl-->>CMux: V2CallResult payload (terminals)
    CMux->>CMux: format payload (JSON or human-readable)
    CMux-->>CLI: output formatted debug info
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly Related PRs

Poem

🐰 I hop through terminals, notes tucked in my fur,

I timestamp each birth and every gentle blur.
I file teardown reasons, map panes and their light,
I whisper to the CLI so you can see the sight.
Hooray — small hops make big debug nights.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.45% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding a hidden CLI command for terminal debugging, matching the core objective.
Description check ✅ Passed The PR description includes a clear summary of changes and verification steps, but lacks structured testing section and incomplete checklist items per template.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch task-cli-ghost-terminal-debug
📝 Coding Plan
  • Generate coding plan for human review comments

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.

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

No issues found across 5 files

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

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

Inline comments:
In `@cmuxTests/CLIProcessRunnerTests.swift`:
- Around line 3-6: The conditional import block using `#if` canImport(cmux_DEV) /
`#elseif` canImport(cmux) must be closed immediately after the import statements
so the test class CLIProcessRunnerTests is always compiled; move the `#endif` that
currently closes the conditional at the end of the file to directly follow the
import directives (i.e., right before the start of the CLIProcessRunnerTests
class) so the imports remain conditional but the test class is outside that
conditional.

In `@Sources/TerminalController.swift`:
- Around line 4969-4980: The current fallback only parses identifiers in
windowIdFromIdentifier(_:), which ignores windows that have plain identifiers or
that already exist in the enumerated windows list; update the lookup to first
try to find a match in the existing windows array returned by
app.scriptableMainWindows() (by object identity or by matching
window.windowNumber/windowId) before attempting to parse the "cmux.main.<uuid>"
prefix. Concretely, when resolving a NSWindow to a UUID (used where
windowIdFromIdentifier(_:), windows, and windowIndexById are referenced), check
the enumerated windows for the same NSWindow instance or same windowNumber and
return that window's windowId/window index if present; only if no match is found
fall back to parsing raw.identifier rawValue for the "cmux.main.<uuid>" pattern.
- Around line 4950-4956: The rectPayload helper currently builds a [String:
Double] from a CGRect whose components are CGFloat; update rectPayload(_ rect:
CGRect) to explicitly convert each component to Double (e.g.
Double(rect.origin.x), Double(rect.origin.y), Double(rect.size.width),
Double(rect.size.height)) so the dictionary values match the declared Double
type and the code type-checks.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8d19e25f-e210-4b00-83ab-c311e304bee7

📥 Commits

Reviewing files that changed from the base of the PR and between e158258 and 0d6b453.

📒 Files selected for processing (5)
  • CLI/cmux.swift
  • GhosttyTabs.xcodeproj/project.pbxproj
  • Sources/GhosttyTerminalView.swift
  • Sources/TerminalController.swift
  • cmuxTests/CLIProcessRunnerTests.swift

Comment thread cmuxTests/CLIProcessRunnerTests.swift Outdated
Comment thread Sources/TerminalController.swift
Comment thread Sources/TerminalController.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: 2

🧹 Nitpick comments (1)
CLI/cmux.swift (1)

3016-3033: Use the existing coercion helpers for rect/ports formatting too.

formatDebugRect and formatDebugPorts can still drop valid numeric payloads in mixed-type dictionaries; using the coercion helper keeps output stable.

Suggested refactor
     private func formatDebugRect(_ value: Any?) -> String? {
         guard let rect = value as? [String: Any],
-              let x = rect["x"] as? Double,
-              let y = rect["y"] as? Double,
-              let width = rect["width"] as? Double,
-              let height = rect["height"] as? Double else {
+              let x = doubleFromAny(rect["x"]),
+              let y = doubleFromAny(rect["y"]),
+              let width = doubleFromAny(rect["width"]),
+              let height = doubleFromAny(rect["height"]) else {
             return nil
         }
         return String(format: "{%.1f,%.1f %.1fx%.1f}", x, y, width, height)
     }

     private func formatDebugPorts(_ value: Any?) -> String {
         guard let array = value as? [Any], !array.isEmpty else { return "[]" }
-        return array
+        let ports = array
             .compactMap { intFromAny($0) }
             .map(String.init)
-            .joined(separator: ",")
+        return ports.isEmpty ? "[]" : ports.joined(separator: ",")
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CLI/cmux.swift` around lines 3016 - 3033, The current formatDebugRect and
formatDebugPorts drop valid numeric values when types vary; update them to use
the existing coercion helpers (e.g., doubleFromAny for rect components and
intFromAny for ports) instead of direct casting. In formatDebugRect, extract
x,y,width,height via doubleFromAny(rect["x"]), etc., guard that those return
non-nil and then format with String(format:...), and in formatDebugPorts map
array elements through intFromAny, filter out nils, return "[]" if the resulting
list is empty, otherwise join with ","; keep the same function names
(formatDebugRect, formatDebugPorts) and reuse intFromAny/doubleFromAny helper
symbols to locate the helpers.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@CLI/cmux.swift`:
- Around line 1852-1859: The "debug-terminals" subcommand currently ignores any
trailing tokens; update the case "debug-terminals" branch to reject unexpected
arguments by checking that there are no extra command tokens/arguments before
calling client.sendV2. If extra args are present, print a concise usage/error
message and exit with a non-zero status (matching existing CLI error handling),
otherwise proceed to call client.sendV2(...) and print either jsonString(...) or
formatDebugTerminalsPayload(...). Ensure you reference the same variables used
in this block (jsonOutput, client.sendV2, formatDebugTerminalsPayload, idFormat)
and reuse the project's standard error/exit helpers for consistency.

In `@Sources/GhosttyTerminalView.swift`:
- Around line 2565-2568: The debug metadata fields (createdAt,
runtimeSurfaceCreatedAt, teardownRequestedAt, teardownRequestReason) are
accessed from background socket handlers via v2DebugTerminals/v2MainSync while
mutations occur off-main (e.g.
recordTeardownRequest()/beginPortalCloseLifecycle()), causing unsynchronized
cross-thread access; fix by making access to these fields main-isolated and
ensuring mutations happen on main: mark the properties (createdAt,
runtimeSurfaceCreatedAt, teardownRequestedAt, teardownRequestReason) as
`@MainActor` or move them into a `@MainActor` extension/actor, and annotate/move
mutating methods like recordTeardownRequest() and runtimeSurfaceCreatedAt =
Date() (or call sites such as beginPortalCloseLifecycle()) to run on the
MainActor (or dispatch them to main) so v2MainSync reads and all writes are
serialized and race-free; keep TerminalSurfaceRegistry/v2DebugTerminals
unchanged except for relying on the main isolation.

---

Nitpick comments:
In `@CLI/cmux.swift`:
- Around line 3016-3033: The current formatDebugRect and formatDebugPorts drop
valid numeric values when types vary; update them to use the existing coercion
helpers (e.g., doubleFromAny for rect components and intFromAny for ports)
instead of direct casting. In formatDebugRect, extract x,y,width,height via
doubleFromAny(rect["x"]), etc., guard that those return non-nil and then format
with String(format:...), and in formatDebugPorts map array elements through
intFromAny, filter out nils, return "[]" if the resulting list is empty,
otherwise join with ","; keep the same function names (formatDebugRect,
formatDebugPorts) and reuse intFromAny/doubleFromAny helper symbols to locate
the helpers.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c47f6d30-35e6-436b-8be3-816759705534

📥 Commits

Reviewing files that changed from the base of the PR and between 0d6b453 and 1a0057a.

📒 Files selected for processing (4)
  • CLI/cmux.swift
  • Sources/GhosttyTerminalView.swift
  • Sources/TerminalController.swift
  • cmuxTests/CLIProcessRunnerTests.swift

Comment thread CLI/cmux.swift
Comment thread Sources/GhosttyTerminalView.swift
@lawrencecchen
lawrencecchen merged commit 8d8fadb into main Mar 17, 2026
12 of 13 checks passed
@lawrencecchen
lawrencecchen deleted the task-cli-ghost-terminal-debug branch March 17, 2026 11:03
bn-l pushed a commit to bn-l/cmux that referenced this pull request Apr 3, 2026
* Add hidden terminal debug CLI command

* Expand orphan terminal debug metadata

* Remove stray CLIProcessRunner test target wiring

* Tighten debug terminal diagnostics handling

---------

Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>

This branch was successfully deployed

1 active deployment
Preview — f5774a84 Deployed Mar 17, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant