Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 55 additions & 1 deletion CLI/cmux.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11550,13 +11550,66 @@ struct CMUXCLI {
return nil
}

private let tmuxShortFormatAliases: [Character: String] = [
"D": "pane_id",
"F": "window_flags",
"I": "window_index",
"P": "pane_index",
"S": "session_name",
"W": "window_name"
]

private func tmuxExpandShortFormatAliases(
_ format: String,
context: [String: String]
) -> String {
guard format.contains("#") else { return format }

var rendered = ""
rendered.reserveCapacity(format.count)
var index = format.startIndex
while index < format.endIndex {
let character = format[index]
guard character == "#" else {
rendered.append(character)
index = format.index(after: index)
continue
}

let nextIndex = format.index(after: index)
guard nextIndex < format.endIndex else {
rendered.append(character)
break
}

let nextCharacter = format[nextIndex]
if nextCharacter == "#" {
rendered.append("##")
index = format.index(after: nextIndex)
continue
}

if let key = tmuxShortFormatAliases[nextCharacter],
let value = context[key] {
rendered.append(value)
index = format.index(after: nextIndex)
continue
}

rendered.append(character)
index = nextIndex
}

return rendered
}

private func tmuxRenderFormat(
_ format: String?,
context: [String: String],
fallback: String
) -> String {
guard let format, !format.isEmpty else { return fallback }
var rendered = format
var rendered = tmuxExpandShortFormatAliases(format, context: context)
for (key, value) in context {
rendered = rendered.replacingOccurrences(of: "#{\(key)}", with: value)
}
Expand All @@ -11578,6 +11631,7 @@ struct CMUXCLI {
let canonicalWorkspaceId = try resolveWorkspaceId(workspaceId, client: client)
var context: [String: String] = [
"session_name": "cmux",
"window_flags": "*",
"window_id": "@\(canonicalWorkspaceId)",
"window_uuid": canonicalWorkspaceId
]
Expand Down
45 changes: 44 additions & 1 deletion daemon/remote/cmd/cmuxd-remote/tmux_compat.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,11 +168,54 @@ func parseTmuxArgs(args []string, valueFlags, boolFlags []string) *tmuxParsed {

var tmuxFormatVarRe = regexp.MustCompile(`#\{[^}]+\}`)

var tmuxShortFormatAliases = map[byte]string{
'D': "pane_id",
'F': "window_flags",
'I': "window_index",
'P': "pane_index",
'S': "session_name",
'W': "window_name",
}

func tmuxExpandShortFormatAliases(format string, context map[string]string) string {
if format == "" {
return format
}

var builder strings.Builder
builder.Grow(len(format))
for i := 0; i < len(format); i++ {
ch := format[i]
if ch != '#' || i+1 >= len(format) {
builder.WriteByte(ch)
continue
}

next := format[i+1]
if next == '#' {
builder.WriteString("##")
i++
continue
}
if key, ok := tmuxShortFormatAliases[next]; ok {
if value, exists := context[key]; exists {
builder.WriteString(value)
i++
continue
}
}
Comment on lines +200 to +206

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.

P2 Known alias but missing context key silently passes through

When a recognized alias (e.g. #I) appears in the format string but its context key (window_index) is absent from the map, the # is emitted and the loop advances to the alias letter, which is then emitted on the next iteration — reproducing the original #I literally. This passthrough is reasonable, but it diverges slightly from real tmux which would render an empty string. A brief comment clarifying the intended behavior would help future readers distinguish a deliberate design choice from a bug.


builder.WriteByte(ch)
}

return builder.String()
}

func tmuxRenderFormat(format string, context map[string]string, fallback string) string {
if format == "" {
return fallback
}
rendered := format
rendered := tmuxExpandShortFormatAliases(format, context)
for key, value := range context {
rendered = strings.ReplaceAll(rendered, "#{"+key+"}", value)
}
Expand Down
14 changes: 11 additions & 3 deletions daemon/remote/cmd/cmuxd-remote/tmux_compat_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,14 @@ func TestParseTmuxArgsClusteredValueFlag(t *testing.T) {

func TestTmuxRenderFormat(t *testing.T) {
ctx := map[string]string{
"pane_id": "%abc123",
"pane_width": "80",
"window_id": "@ws1",
"session_name": "cmux",
"pane_id": "%abc123",
"pane_index": "2",
"pane_width": "80",
"window_flags": "*",
"window_id": "@ws1",
"window_index": "4",
"window_name": "editor",
}

tests := []struct {
Expand All @@ -80,6 +85,9 @@ func TestTmuxRenderFormat(t *testing.T) {
}{
{"#{pane_id}", "fallback", "%abc123"},
{"#{pane_id}:#{pane_width}", "", "%abc123:80"},
{"#S:#I.#P #W", "", "cmux:4.2 editor"},
{"#F #D", "", "* %abc123"},
{"##S #S", "", "##S cmux"},
{"#{unknown_var}", "fallback", "fallback"},
{"", "fallback", "fallback"},
{"#{pane_id} #{pane_width} #{window_id}", "", "%abc123 80 @ws1"},
Expand Down
4 changes: 4 additions & 0 deletions tests_v2/test_tmux_compat_matrix.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,10 @@ def main() -> int:
shown = _run_cli(cli, ["display-message", "-p", msg])
_must(msg in shown.stdout, f"display-message -p should print message: {shown.stdout!r}")

short_aliases = _run_cli(cli, ["display-message", "-p", "#S:#I.#P #W"])
_must(short_aliases.stdout.strip() == f"cmux:0.0 {ws}",
f"display-message short aliases should expand: {short_aliases.stdout!r}")
Comment on lines +273 to +274

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.

P1 #W asserted against workspace UUID instead of workspace title

ws is the UUID returned by c.new_workspace(), but #W expands to window_name, which is set from the workspace's title field (see tmuxFormatContext in tmux_compat.go, line ~259). By this point in the test the workspace has been renamed to title = f"tmux-title-{stamp}", so #W would expand to that renamed title — not the UUID — causing this assertion to always fail.

Suggested change
_must(short_aliases.stdout.strip() == f"cmux:0.0 {ws}",
f"display-message short aliases should expand: {short_aliases.stdout!r}")
short_aliases = _run_cli(cli, ["display-message", "-p", "#S:#I.#P #W"])
_must(short_aliases.stdout.strip() == f"cmux:0.0 {title}",
f"display-message short aliases should expand: {short_aliases.stdout!r}")

Comment on lines +272 to +274

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.

P2 Hardcoded window_index=0 and pane_index=0 are fragile

The assertion "cmux:0.0 {title}" assumes #I (window_index) is 0 and #P (pane_index) is also 0. Window index depends on the order workspaces are listed by the daemon, which can be affected by pre-existing workspaces. Pane index after break-pane / join-pane / swap-pane operations may also not be 0. Consider reading back the actual index values from display-message #{window_index} and #{pane_index} and interpolating them into the expected string, or at least asserting that #S and #W expand correctly in isolation.

Comment on lines +272 to +274

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Avoid hard-coding dynamic tmux context values in this regression assertion.

At Line 184 the test renames the window, and #I/#P/#W depend on current runtime context. Asserting cmux:0.0 {ws} makes this check brittle and can fail despite correct short-alias expansion.

Suggested fix
-        short_aliases = _run_cli(cli, ["display-message", "-p", "#S:`#I`.#P `#W`"])
-        _must(short_aliases.stdout.strip() == f"cmux:0.0 {ws}",
-              f"display-message short aliases should expand: {short_aliases.stdout!r}")
+        long_form = _run_cli(
+            cli,
+            ["display-message", "-p", "#{session_name}:#{window_index}.#{pane_index} #{window_name}"],
+        )
+        short_aliases = _run_cli(cli, ["display-message", "-p", "#S:`#I`.#P `#W`"])
+        _must(
+            short_aliases.stdout.strip() == long_form.stdout.strip(),
+            f"display-message short aliases should match long-form render: short={short_aliases.stdout!r} long={long_form.stdout!r}",
+        )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests_v2/test_tmux_compat_matrix.py` around lines 272 - 274, The assertion
hard-codes dynamic tmux context values; update the check around short_aliases
(result of _run_cli) so it doesn't require fixed indices. Instead parse or
regex-match short_aliases.stdout.strip(): verify the part after the space equals
the expected window name variable ws, and verify the prefix matches the pattern
"cmux:<number>.<number>" (e.g., with a regex like r"^cmux:\d+\.\d+$" applied to
the first token). Use the existing _must helper to assert both conditions
against short_aliases.stdout.strip() so the test no longer depends on exact
runtime `#I/`#P values.


print("PASS: tmux compatibility matrix commands are wired and tested")
return 0

Expand Down