railmux 0.4.2.dev4__tar.gz → 0.4.2.dev6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (208) hide show
  1. {railmux-0.4.2.dev4/src/railmux.egg-info → railmux-0.4.2.dev6}/PKG-INFO +1 -1
  2. railmux-0.4.2.dev6/src/railmux/__init__.py +1 -0
  3. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/fast_display_server.py +42 -21
  4. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/models.py +6 -0
  5. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/session_cache.py +54 -0
  6. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/session_index.py +20 -6
  7. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/transcript.py +272 -43
  8. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/ui/app.py +82 -2
  9. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6/src/railmux.egg-info}/PKG-INFO +1 -1
  10. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux.egg-info/SOURCES.txt +1 -0
  11. railmux-0.4.2.dev6/tests/test_claude_continuation.py +177 -0
  12. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_fast_display_server.py +32 -4
  13. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_transcript.py +76 -0
  14. railmux-0.4.2.dev4/src/railmux/__init__.py +0 -1
  15. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/LICENSE +0 -0
  16. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/MANIFEST.in +0 -0
  17. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/README.md +0 -0
  18. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/pyproject.toml +0 -0
  19. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/setup.cfg +0 -0
  20. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/__main__.py +0 -0
  21. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/atomic_file.py +0 -0
  22. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/background_index.py +0 -0
  23. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/cli.py +0 -0
  24. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/codex_index.py +0 -0
  25. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/config.py +0 -0
  26. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/config_cli.py +0 -0
  27. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/diagnostic_contract.py +0 -0
  28. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/diagnostics.py +0 -0
  29. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/discovery.py +0 -0
  30. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/display_transport.py +0 -0
  31. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/entrypoint.py +0 -0
  32. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/fast_display_client.py +0 -0
  33. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/fast_display_history.py +0 -0
  34. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/fast_display_input.py +0 -0
  35. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/fast_display_protocol.py +0 -0
  36. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/favorites.py +0 -0
  37. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/fuzzy.py +0 -0
  38. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/help_workspace.py +0 -0
  39. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/image_paste.py +0 -0
  40. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/launcher.py +0 -0
  41. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/legacy_sessions.py +0 -0
  42. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/local_clipboard.py +0 -0
  43. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/local_open.py +0 -0
  44. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/modes.py +0 -0
  45. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/mouse_manager.py +0 -0
  46. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/orphan_marker.py +0 -0
  47. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/pane_surface.py +0 -0
  48. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/path_codec.py +0 -0
  49. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/preview_pager.py +0 -0
  50. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/project_favorites.py +0 -0
  51. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/provider_paths.py +0 -0
  52. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/release_version.py +0 -0
  53. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/remote_config.py +0 -0
  54. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/renames.py +0 -0
  55. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/restart_state.py +0 -0
  56. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/runtime_config.py +0 -0
  57. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/scroll_agent.py +0 -0
  58. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/scroll_manager.py +0 -0
  59. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/selection_isolation.py +0 -0
  60. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/self_update.py +0 -0
  61. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/session_lease.py +0 -0
  62. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/setting_contracts.py +0 -0
  63. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/settings.py +0 -0
  64. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/ssh_args.py +0 -0
  65. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/ssh_compat.py +0 -0
  66. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/ssh_display_diagnostics.py +0 -0
  67. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/ssh_doctor.py +0 -0
  68. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/ssh_preflight.py +0 -0
  69. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/system_deps.py +0 -0
  70. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/terminal_screen.py +0 -0
  71. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/terminal_status.py +0 -0
  72. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/tmux_binding_manager.py +0 -0
  73. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/tmux_capabilities.py +0 -0
  74. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/tmux_ctl.py +0 -0
  75. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/tmux_health.py +0 -0
  76. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/tmux_server.py +0 -0
  77. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/tool_panes.py +0 -0
  78. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/ui/__init__.py +0 -0
  79. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/ui/_widgets.py +0 -0
  80. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/ui/keymap.py +0 -0
  81. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/ui/modals.py +0 -0
  82. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/ui/projects_pane.py +0 -0
  83. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/ui/running_pane.py +0 -0
  84. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/ui/sessions_pane.py +0 -0
  85. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/ui/sidebar.py +0 -0
  86. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/ui/statusbar.py +0 -0
  87. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/ui/workspace.py +0 -0
  88. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/windows_attach_relay.py +0 -0
  89. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/windows_bootstrap.py +0 -0
  90. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/windows_install_log.py +0 -0
  91. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/windows_msys2.py +0 -0
  92. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/windows_pacman.py +0 -0
  93. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/windows_paths.py +0 -0
  94. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/windows_tmux_lifecycle.py +0 -0
  95. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux/windows_ui_transition.py +0 -0
  96. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux.egg-info/dependency_links.txt +0 -0
  97. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux.egg-info/entry_points.txt +0 -0
  98. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux.egg-info/requires.txt +0 -0
  99. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/src/railmux.egg-info/top_level.txt +0 -0
  100. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/app_test_harness.py +0 -0
  101. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_agent_workspace.py +0 -0
  102. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_app_codex_placeholder.py +0 -0
  103. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_app_quit.py +0 -0
  104. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_app_restart_restore.py +0 -0
  105. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_app_running_recovery.py +0 -0
  106. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_atomic_file.py +0 -0
  107. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_background_index.py +0 -0
  108. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_cli.py +0 -0
  109. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_code_map.py +0 -0
  110. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_codex_delete.py +0 -0
  111. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_codex_index.py +0 -0
  112. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_codex_lineage.py +0 -0
  113. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_codex_transcript.py +0 -0
  114. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_config.py +0 -0
  115. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_config_cli.py +0 -0
  116. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_denested_transport_benchmark.py +0 -0
  117. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_diagnostics.py +0 -0
  118. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_discovery.py +0 -0
  119. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_display_transport.py +0 -0
  120. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_fast_display_bootstrap.py +0 -0
  121. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_fast_display_history.py +0 -0
  122. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_fast_display_protocol.py +0 -0
  123. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_fast_display_selection.py +0 -0
  124. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_fast_display_terminal.py +0 -0
  125. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_focus_highlight.py +0 -0
  126. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_fullscreen_binding.py +0 -0
  127. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_fuzzy.py +0 -0
  128. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_help_agent.py +0 -0
  129. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_help_workspace.py +0 -0
  130. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_history_cleanup.py +0 -0
  131. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_image_paste.py +0 -0
  132. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_keymap.py +0 -0
  133. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_launcher.py +0 -0
  134. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_layout_profile.py +0 -0
  135. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_legacy_sessions.py +0 -0
  136. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_local_clipboard.py +0 -0
  137. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_local_open.py +0 -0
  138. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_modals.py +0 -0
  139. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_mode_selection.py +0 -0
  140. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_modes.py +0 -0
  141. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_mouse_manager.py +0 -0
  142. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_new_project.py +0 -0
  143. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_options.py +0 -0
  144. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_orphan_recovery.py +0 -0
  145. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_pane_surface.py +0 -0
  146. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_path_codec.py +0 -0
  147. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_preview_pager.py +0 -0
  148. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_project_favorites.py +0 -0
  149. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_projects_pane.py +0 -0
  150. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_provider_paths.py +0 -0
  151. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_release_notes.py +0 -0
  152. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_release_version.py +0 -0
  153. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_release_workflow.py +0 -0
  154. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_remote_config.py +0 -0
  155. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_renames.py +0 -0
  156. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_restart_state.py +0 -0
  157. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_running_pane.py +0 -0
  158. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_running_sort.py +0 -0
  159. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_runtime_config.py +0 -0
  160. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_scroll_agent.py +0 -0
  161. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_scroll_manager.py +0 -0
  162. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_selection_isolation.py +0 -0
  163. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_self_update.py +0 -0
  164. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_session_cache.py +0 -0
  165. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_session_index.py +0 -0
  166. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_session_lease.py +0 -0
  167. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_sessions_pane.py +0 -0
  168. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_setting_contracts.py +0 -0
  169. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_settings.py +0 -0
  170. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_sidebar.py +0 -0
  171. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_ssh_args.py +0 -0
  172. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_ssh_compat.py +0 -0
  173. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_ssh_display_benchmark.py +0 -0
  174. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_ssh_display_diagnostics.py +0 -0
  175. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_ssh_doctor.py +0 -0
  176. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_status.py +0 -0
  177. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_statusbar.py +0 -0
  178. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_system_deps.py +0 -0
  179. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_terminal_size.py +0 -0
  180. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_terminal_status.py +0 -0
  181. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_termux_local_touch.py +0 -0
  182. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_tmux_binding_manager.py +0 -0
  183. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_tmux_capabilities.py +0 -0
  184. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_tmux_ctl.py +0 -0
  185. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_tmux_health.py +0 -0
  186. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_tmux_integration.py +0 -0
  187. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_tmux_server.py +0 -0
  188. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_tmux_status.py +0 -0
  189. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_tool_panes.py +0 -0
  190. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_utf8_state.py +0 -0
  191. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_web_demo_recorder.py +0 -0
  192. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_wheel_smoke.py +0 -0
  193. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_widgets.py +0 -0
  194. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_windows_attach_relay.py +0 -0
  195. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_windows_bootstrap.py +0 -0
  196. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_windows_install_log.py +0 -0
  197. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_windows_msys2.py +0 -0
  198. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_windows_pacman.py +0 -0
  199. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_windows_paths.py +0 -0
  200. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_windows_synchronized_screen.py +0 -0
  201. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_windows_tmux_lifecycle.py +0 -0
  202. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_windows_ui_transition.py +0 -0
  203. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_windows_wrapper_contract.py +0 -0
  204. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tests/test_yolo_prompt.py +0 -0
  205. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tools/denested_transport_benchmark.py +0 -0
  206. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tools/release_notes.py +0 -0
  207. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tools/wheel_smoke.py +0 -0
  208. {railmux-0.4.2.dev4 → railmux-0.4.2.dev6}/tools/windows_archive_smoke.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: railmux
3
- Version: 0.4.2.dev4
3
+ Version: 0.4.2.dev6
4
4
  Summary: Terminal UI to navigate, resume, and start Claude Code and Codex sessions
5
5
  Author-email: Rightglow <zhang.taian@foxmail.com>
6
6
  License-Expression: MIT
@@ -0,0 +1 @@
1
+ __version__ = "0.4.2.dev6"
@@ -374,7 +374,7 @@ class _TranscriptFormatCacheEntry:
374
374
  _TRANSCRIPT_CACHE: OrderedDict[tuple[str, int], _TranscriptCacheEntry] = OrderedDict()
375
375
  _TRANSCRIPT_CACHE_LIMIT = 4
376
376
  _TRANSCRIPT_FORMAT_CACHE: OrderedDict[
377
- tuple[str, str], _TranscriptFormatCacheEntry
377
+ tuple[str, str, int | None], _TranscriptFormatCacheEntry
378
378
  ] = OrderedDict()
379
379
  _TRANSCRIPT_FORMAT_CACHE_LIMIT = 2
380
380
  _TRANSCRIPT_FORMAT_CACHE_MAX_CHARS = 4 * 1024 * 1024
@@ -1699,7 +1699,9 @@ def _transcript_rows(
1699
1699
  ):
1700
1700
  _TRANSCRIPT_CACHE.move_to_end(key)
1701
1701
  return cached
1702
- format_key = (str(source.path), source.provider)
1702
+ # Claude history is pre-wrapped to the pane; Codex text is not.
1703
+ format_width = width if source.provider == "claude" else None
1704
+ format_key = (str(source.path), source.provider, format_width)
1703
1705
  formatted_entry = _TRANSCRIPT_FORMAT_CACHE.get(format_key)
1704
1706
  if formatted_entry is not None and formatted_entry.identity == identity:
1705
1707
  _TRANSCRIPT_FORMAT_CACHE.move_to_end(format_key)
@@ -1715,6 +1717,7 @@ def _transcript_rows(
1715
1717
  io.StringIO(raw),
1716
1718
  source.provider,
1717
1719
  claude_native=source.provider == "claude",
1720
+ width=format_width,
1718
1721
  )
1719
1722
  )
1720
1723
  if len(formatted) <= _TRANSCRIPT_FORMAT_CACHE_MAX_CHARS:
@@ -1773,10 +1776,12 @@ def _transcript_rows_before_screen(
1773
1776
 
1774
1777
  Transcript-backed history appends the live viewport after the transcript,
1775
1778
  whose tail usually holds the same newest message. Locate the screen's
1776
- text in the transcript tail through distinctive anchor rows and cut the
1777
- transcript where the screen begins. Two anchors must agree on that start
1778
- unless one long anchor is the only candidate; without a credible match
1779
- every transcript row is kept.
1779
+ text in the transcript tail through distinctive anchor rows: the largest
1780
+ group of anchors that agree on where the screen starts wins, so screen
1781
+ regions the transcript renders differently (such as a diff) cannot veto
1782
+ the match. A lone anchor must be long and unopposed. From the earliest agreeing anchor
1783
+ the cut walks back over screen rows that also precede it in the
1784
+ transcript; without a credible match every transcript row is kept.
1780
1785
  """
1781
1786
  total = len(transcript_rows)
1782
1787
  window = max(
@@ -1791,26 +1796,42 @@ def _transcript_rows_before_screen(
1791
1796
  parts.append(text)
1792
1797
  offset += len(text)
1793
1798
  haystack = "".join(parts)
1799
+ screen_texts = [_overlap_text(row) for row in screen_rows]
1794
1800
  screen_offset = 0
1795
- starts: list[tuple[int, int]] = []
1796
- for row in screen_rows:
1797
- text = _overlap_text(row)
1801
+ # (screen row, transcript position, anchor length, implied screen start)
1802
+ anchors: list[tuple[int, int, int, int]] = []
1803
+ for index, text in enumerate(screen_texts):
1798
1804
  if len(text) >= _OVERLAP_ANCHOR_CHARS and haystack.count(text) == 1:
1799
- starts.append((haystack.index(text) - screen_offset, len(text)))
1800
- if len(starts) == 2:
1801
- break
1805
+ position = haystack.index(text)
1806
+ anchors.append(
1807
+ (index, position, len(text), position - screen_offset))
1802
1808
  screen_offset += len(text)
1803
- if not starts:
1809
+ if not anchors:
1804
1810
  return total
1805
- if len(starts) == 1:
1806
- if starts[0][1] < _OVERLAP_SINGLE_ANCHOR_CHARS:
1807
- return total
1808
- else:
1811
+
1812
+ def agreeing(anchor: tuple[int, int, int, int]) -> list:
1809
1813
  # Allow small rendering differences (list numbering, bullets).
1810
- tolerance = 16 + abs(starts[0][0]) // 20
1811
- if abs(starts[0][0] - starts[1][0]) > tolerance:
1812
- return total
1813
- start = max(0, starts[0][0])
1814
+ tolerance = 16 + abs(anchor[3]) // 20
1815
+ return [
1816
+ other for other in anchors
1817
+ if abs(other[3] - anchor[3]) <= tolerance
1818
+ ]
1819
+
1820
+ group = max(
1821
+ (agreeing(anchor) for anchor in anchors),
1822
+ key=lambda members: (len(members), max(m[3] for m in members)),
1823
+ )
1824
+ if len(group) == 1 and (
1825
+ len(anchors) > 1 or group[0][2] < _OVERLAP_SINGLE_ANCHOR_CHARS
1826
+ ):
1827
+ return total
1828
+ index, start, _length, _implied = min(group)
1829
+ for text in reversed(screen_texts[:index]):
1830
+ if not text:
1831
+ continue
1832
+ if start < len(text) or haystack[start - len(text):start] != text:
1833
+ break
1834
+ start -= len(text)
1814
1835
  # Keep a row that begins before the screen: it may carry text the screen
1815
1836
  # no longer shows, and a partial duplicate is better than a loss.
1816
1837
  keep = len(row_starts)
@@ -84,6 +84,12 @@ class SessionMeta:
84
84
  # A Railmux lease observed in the same shared provider history root. This
85
85
  # is a read-time UI overlay, never provider transcript metadata.
86
86
  remote_owner: str | None = None
87
+ # Successor named by a final Claude ``continued-in`` record: the live
88
+ # conversation moved to that session file. Always None for Codex.
89
+ continued_in_id: str | None = None
90
+ # Claude Code background-job (``sessionKind: bg``) session. Listings hide
91
+ # these unless one is the ``continued-in`` successor of a visible session.
92
+ background: bool = False
87
93
 
88
94
  @property
89
95
  def display_title(self) -> str:
@@ -33,6 +33,9 @@ class SessionCache:
33
33
  # User-assigned titles, overlaid at read time so they survive Claude
34
34
  # Code rewriting its own ai-title record every turn.
35
35
  self._renames = renames
36
+ # Claude ``continued-in`` links seen by listings: predecessor id ->
37
+ # successor id. The App uses them to move live bindings and favorites.
38
+ self.continuations: dict[str, str] = {}
36
39
 
37
40
  def list_sessions(
38
41
  self, project: Project, top_n: int = _DEFAULT_TOP_N
@@ -86,9 +89,60 @@ class SessionCache:
86
89
  if stale.parent == project.claude_dir and stale not in current_paths:
87
90
  del self._entries[stale]
88
91
 
92
+ results = self._hide_continued(project, results)
89
93
  results.sort(key=lambda s: s.last_mtime, reverse=True)
90
94
  return results
91
95
 
96
+ def _hide_continued(
97
+ self, project: Project, results: list[SessionMeta]
98
+ ) -> list[SessionMeta]:
99
+ """Drop background sessions and sessions continued in a successor.
100
+
101
+ Claude Code copies the whole conversation into the successor, so the
102
+ predecessor is a stale duplicate. A user rename moves forward with it.
103
+ A background successor of a visible session is the same interactive
104
+ conversation now hosted by Claude's daemon, so it stays visible.
105
+ """
106
+ by_id = {meta.session_id: meta for meta in results}
107
+ visible = {meta.session_id for meta in results if not meta.background}
108
+ pending = list(visible)
109
+ while pending:
110
+ successor = by_id[pending.pop()].continued_in_id
111
+ if successor in by_id and successor not in visible:
112
+ visible.add(successor)
113
+ pending.append(successor)
114
+ results = [meta for meta in results if meta.session_id in visible]
115
+ hidden: dict[str, str] = {}
116
+ for meta in results:
117
+ successor = meta.continued_in_id
118
+ if (
119
+ successor is not None
120
+ and successor != meta.session_id
121
+ and (project.claude_dir / f"{successor}.jsonl").is_file()
122
+ ):
123
+ hidden[meta.session_id] = successor
124
+ if not hidden:
125
+ return results
126
+ self.continuations.update(hidden)
127
+ carried: set[str] = set()
128
+ if self._renames is not None:
129
+ # Walk each chain forward so A -> B -> C carries A's rename to C.
130
+ for _ in range(len(hidden)):
131
+ changed = False
132
+ for predecessor, successor in hidden.items():
133
+ title = self._renames.get(predecessor)
134
+ if title and not self._renames.get(successor):
135
+ self._renames.set(successor, title)
136
+ carried.add(successor)
137
+ changed = True
138
+ if not changed:
139
+ break
140
+ return [
141
+ self._with_override(meta) if meta.session_id in carried else meta
142
+ for meta in results
143
+ if meta.session_id not in hidden
144
+ ]
145
+
92
146
  def get(self, project: Project, session_id: str) -> SessionMeta | None:
93
147
  """Cache-backed lookup of a single session's metadata by id.
94
148
 
@@ -43,6 +43,7 @@ class _SessionScanState:
43
43
  last_rtype: str = ""
44
44
  last_stop_reason: str = ""
45
45
  background: bool = False
46
+ continued_in: str | None = None
46
47
 
47
48
  def clone(self) -> _SessionScanState:
48
49
  return _SessionScanState(
@@ -62,6 +63,7 @@ class _SessionScanState:
62
63
  last_rtype=self.last_rtype,
63
64
  last_stop_reason=self.last_stop_reason,
64
65
  background=self.background,
66
+ continued_in=self.continued_in,
65
67
  )
66
68
 
67
69
 
@@ -169,14 +171,25 @@ def _can_resume(fd: int, st: os.stat_result, state: _SessionScanState) -> bool:
169
171
 
170
172
 
171
173
  def _consume_record(state: _SessionScanState, rec: object) -> None:
172
- if not isinstance(rec, dict) or state.background:
174
+ if not isinstance(rec, dict):
173
175
  return
174
176
  # Background-job sessions are not interactive — they can't be resumed in
175
- # a terminal and shouldn't appear in the sidebar.
177
+ # a terminal and shouldn't appear in the sidebar. Their metadata is still
178
+ # collected because Claude Code also hosts a live conversation that was
179
+ # ``continued-in`` a daemon session this way; listings decide visibility.
176
180
  if rec.get("sessionKind") == "bg":
177
181
  state.background = True
178
- return
179
182
  rtype = rec.get("type")
183
+ # Claude Code ends a session file with ``continued-in`` when the live
184
+ # conversation moves to a new session. Only a final record counts.
185
+ successor = rec.get("continuedInSessionId")
186
+ state.continued_in = (
187
+ successor
188
+ if rtype == "continued-in"
189
+ and isinstance(successor, str)
190
+ and _looks_like_uuid(successor)
191
+ else None
192
+ )
180
193
  if rtype == "ai-title":
181
194
  state.title = rec.get("aiTitle") or state.title
182
195
  # NOTE: "last-prompt" is deliberately NOT treated as a turn. Claude Code
@@ -235,8 +248,6 @@ def _meta_from_state(
235
248
  state: _SessionScanState,
236
249
  st: os.stat_result,
237
250
  ) -> SessionMeta | None:
238
- if state.background:
239
- return None
240
251
  assistant_count = len(state.assistant_token_totals)
241
252
  message_count = state.user_count + assistant_count
242
253
  if message_count == 0 or (state.user_count > 0 and assistant_count == 0):
@@ -277,6 +288,8 @@ def _meta_from_state(
277
288
  last_user_message=preview,
278
289
  status=status,
279
290
  pending_tool=pending_tool,
291
+ continued_in_id=state.continued_in,
292
+ background=state.background,
280
293
  )
281
294
 
282
295
 
@@ -352,7 +365,8 @@ def _scan_session_incremental(
352
365
 
353
366
 
354
367
  def _scan_session(project: Project, jsonl_path: Path) -> SessionMeta | None:
355
- return _scan_session_incremental(project, jsonl_path).meta
368
+ meta = _scan_session_incremental(project, jsonl_path).meta
369
+ return None if meta is not None and meta.background else meta
356
370
 
357
371
 
358
372
  def _looks_like_uuid(s: str) -> bool: