Skip to content

search_panes: visual-row capture misses wrap-spanning patterns #55

Closed

Description

@tony

Problem

search_panes doesn't join visually-wrapped lines before matching, so plain-text patterns that tmux wraps at the pane's column width split across two captured rows and miss the match. Long error messages (e.g. "Build failed: module not found in /very/long/path/to/module.py") are the canonical example — they wrap mid-string and re.search against each row in isolation never sees the full string.

wait_for_text shipped the wrap fix in #45 (commit 67eed15) by passing join_wrapped=True to pane.capture_pane() — which adds tmux's -J flag. search_panes was not part of that sweep.

Repro

Render a pane with a long line that wraps:

$ pytest tests/integration/test_pipeline_with_a_very_long_test_class_name_to_force_wrap.py::TestXYZ::test_specific_assertion_that_fails
FAILED

If the pane is ~80 cols wide, the path wraps. Then:

search_panes(pattern="test_specific_assertion_that_fails")

Returns the pane (the substring lives on row 2). But:

search_panes(pattern="tests/integration/test_pipeline_with_a_very_long_test_class_name_to_force_wrap.py::TestXYZ::test_specific_assertion_that_fails")

Returns no match — the pattern spans the wrap boundary.

Root cause

src/libtmux_mcp/tools/pane_tools/search.py slow path:

lines = pane.capture_pane(start=content_start, end=content_end)
matched_lines = [line for line in lines if compiled.search(line)]

No join_wrapped=True. Compare to src/libtmux_mcp/tools/pane_tools/wait.py:

lines = await asyncio.to_thread(
    pane.capture_pane,
    start=start_line,
    end=None,
    join_wrapped=True,
)

Nuance — the fast path is structurally wrap-blind

search_panes has two paths:

  1. Slow path (Python-regex over captured lines): the mechanical fix is join_wrapped=True. Drop-in.
  2. Fast path (#{C:pattern} format conditional): runs tmux's C-level window_pane_search() against the grid by visual row. There's no -J analog at the format-conditional level, so this path is wrap-blind by tmux design.

The fast path triggers when (a) no scrollback range is requested and (b) the pattern has no regex metacharacters or tmux format-injection characters. Long plain-text error messages from build output hit the fast path most of the time — exactly the worst case for wrap-blindness.

Proposed fix

  1. Slow path: add join_wrapped=True to the pane.capture_pane(...) call. Same trade-off wait_for_text accepts — matched_lines may contain strings longer than pane_width.
  2. Fast path: docstring note acknowledging that plain-text patterns spanning the pane's wrap column may miss matches; suggest passing regex=True or supplying scrollback range (content_start=-N) to force the slow path.
  3. Test: regression covering a wrap-spanning pattern, parallel to test_wait_for_text_matches_pattern_across_wrap.
  4. CHANGES entry under ### Fixes (Published-Release Test passes — search_panes shipped in 0.1.0a2).

Out of scope

  • Routing wrap-spanning patterns from the fast path to the slow path automatically. Requires tmux-side detection of pane width vs. pattern length, which is heuristic and adds complexity.
  • Extending -J to tmux's #{C:...} format conditional. That's an upstream-tmux change.

Activity

  1. added
    bugSomething isn't working
    enhancementNew feature or request
    on May 24, 2026
  2. tony commented on May 24, 2026

    @tony
    MemberAuthor

    Triage pass (2026-05-24): keep this as a first-action correctness bug.

    Current-code check: the slow path still captures rows without join_wrapped=True, while wait_for_text already uses joined wrapped rows for the same class of terminal output. libtmux exposes the needed flag on Pane.capture_pane(join_wrapped=True) (libtmux f70cb62). The tmux fast path is inherently visual-row based through window_pane_search() (tmux 134ba6c), so the body's fast-path/slow-path split is the right framing.

    Overlap/value: this is not overshadowed by #60. capture_since would help agents inspect output, but search_panes should still be correct when users ask it to search pane text. Recommended action remains: fix the slow path, document the fast-path wrap limitation, add a wrap-spanning regression, and ship a ### Fixes changelog entry.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingenhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions