comate-cli 0.7.1__tar.gz → 0.7.2__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 (200) hide show
  1. {comate_cli-0.7.1 → comate_cli-0.7.2}/PKG-INFO +1 -1
  2. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/app.py +40 -68
  3. comate_cli-0.7.2/comate_cli/terminal_agent/update_check.py +334 -0
  4. {comate_cli-0.7.1 → comate_cli-0.7.2}/pyproject.toml +7 -1
  5. comate_cli-0.7.2/tests/test_update_check.py +371 -0
  6. {comate_cli-0.7.1 → comate_cli-0.7.2}/uv.lock +1457 -1441
  7. comate_cli-0.7.1/tests/test_update_check.py +0 -201
  8. {comate_cli-0.7.1 → comate_cli-0.7.2}/.gitignore +0 -0
  9. {comate_cli-0.7.1 → comate_cli-0.7.2}/AGENTS.md +0 -0
  10. {comate_cli-0.7.1 → comate_cli-0.7.2}/CHANGELOG.md +0 -0
  11. {comate_cli-0.7.1 → comate_cli-0.7.2}/README.md +0 -0
  12. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/__init__.py +0 -0
  13. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/__main__.py +0 -0
  14. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/main.py +0 -0
  15. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/mcp_cli.py +0 -0
  16. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/__init__.py +0 -0
  17. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/animations.py +0 -0
  18. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/assistant_render.py +0 -0
  19. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/codenames.py +0 -0
  20. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/config/__init__.py +0 -0
  21. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/config/model.py +0 -0
  22. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/config/picker.py +0 -0
  23. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/config/picker_state.py +0 -0
  24. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/config/store.py +0 -0
  25. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/custom_slash_commands.py +0 -0
  26. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/env_utils.py +0 -0
  27. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/error_display.py +0 -0
  28. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/event_renderer.py +0 -0
  29. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/figures.py +0 -0
  30. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/fragment_utils.py +0 -0
  31. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/history_printer.py +0 -0
  32. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/input_geometry.py +0 -0
  33. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/layout_coordinator.py +0 -0
  34. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/logging_adapter.py +0 -0
  35. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/logo.py +0 -0
  36. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/markdown_render.py +0 -0
  37. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/mention_completer.py +0 -0
  38. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/message_style.py +0 -0
  39. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/models.py +0 -0
  40. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/path_context_hint.py +0 -0
  41. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/plugins/__init__.py +0 -0
  42. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/plugins/components/__init__.py +0 -0
  43. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/plugins/components/detail_view.py +0 -0
  44. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/plugins/components/plugin_list.py +0 -0
  45. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/plugins/components/search_box.py +0 -0
  46. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/plugins/components/tab_bar.py +0 -0
  47. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/plugins/marketplace_install_view.py +0 -0
  48. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/plugins/plugin_picker.py +0 -0
  49. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/plugins/tabs/__init__.py +0 -0
  50. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/plugins/tabs/discover_tab.py +0 -0
  51. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/plugins/tabs/errors_tab.py +0 -0
  52. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/plugins/tabs/installed_tab.py +0 -0
  53. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/plugins/tabs/marketplaces_tab.py +0 -0
  54. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/preflight.py +0 -0
  55. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/question_view.py +0 -0
  56. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/resume_picker.py +0 -0
  57. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/resume_preview.py +0 -0
  58. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/resume_selector.py +0 -0
  59. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/rpc_protocol.py +0 -0
  60. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/rpc_stdio.py +0 -0
  61. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/selection_menu.py +0 -0
  62. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/slash_commands.py +0 -0
  63. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/startup.py +0 -0
  64. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/status_bar.py +0 -0
  65. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/statusline/__init__.py +0 -0
  66. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/statusline/model.py +0 -0
  67. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/statusline/picker.py +0 -0
  68. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/statusline/picker_state.py +0 -0
  69. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/statusline/store.py +0 -0
  70. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/text_effects.py +0 -0
  71. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/tips.py +0 -0
  72. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/tool_fold.py +0 -0
  73. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/tool_result_formatters.py +0 -0
  74. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/tool_result_store.py +0 -0
  75. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/tool_result_viewer.py +0 -0
  76. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/tool_view.py +0 -0
  77. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/transcript_viewer.py +0 -0
  78. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/tui.py +0 -0
  79. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/tui_parts/__init__.py +0 -0
  80. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/tui_parts/btw_view.py +0 -0
  81. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/tui_parts/commands.py +0 -0
  82. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/tui_parts/history_sync.py +0 -0
  83. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/tui_parts/input_behavior.py +0 -0
  84. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/tui_parts/key_bindings.py +0 -0
  85. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/tui_parts/mcp_connecting_view.py +0 -0
  86. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/tui_parts/render_panels.py +0 -0
  87. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/tui_parts/slash_command_registry.py +0 -0
  88. {comate_cli-0.7.1 → comate_cli-0.7.2}/comate_cli/terminal_agent/tui_parts/ui_mode.py +0 -0
  89. {comate_cli-0.7.1 → comate_cli-0.7.2}/docs/hooks.md +0 -0
  90. {comate_cli-0.7.1 → comate_cli-0.7.2}/docs/superpowers/plans/2026-04-03-phrase-shuffle.md +0 -0
  91. {comate_cli-0.7.1 → comate_cli-0.7.2}/docs/superpowers/plans/2026-05-22-log-style-implementation-plan.md +0 -0
  92. {comate_cli-0.7.1 → comate_cli-0.7.2}/docs/superpowers/specs/2026-04-01-conditional-diff-subtitle-design.md +0 -0
  93. {comate_cli-0.7.1 → comate_cli-0.7.2}/docs/superpowers/specs/2026-04-03-phrase-shuffle-design.md +0 -0
  94. {comate_cli-0.7.1 → comate_cli-0.7.2}/docs/superpowers/specs/2026-05-22-comate-cli-log-styling-design.md +0 -0
  95. {comate_cli-0.7.1 → comate_cli-0.7.2}/docs/superpowers/specs/2026-05-22-log-style-optimization-design.md +0 -0
  96. {comate_cli-0.7.1 → comate_cli-0.7.2}/report.md +0 -0
  97. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/config/__init__.py +0 -0
  98. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/config/test_model.py +0 -0
  99. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/config/test_picker_state.py +0 -0
  100. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/config/test_picker_ui.py +0 -0
  101. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/config/test_roundtrip.py +0 -0
  102. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/config/test_store_load.py +0 -0
  103. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/config/test_store_save.py +0 -0
  104. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/conftest.py +0 -0
  105. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/fixtures/fake_mcp_misbehaving_stdout.py +0 -0
  106. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/statusline/__init__.py +0 -0
  107. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/statusline/test_model.py +0 -0
  108. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/statusline/test_picker_state.py +0 -0
  109. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/statusline/test_store.py +0 -0
  110. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_animator_shuffle.py +0 -0
  111. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_app_mcp_preload.py +0 -0
  112. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_app_preflight_gate.py +0 -0
  113. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_app_print_mode.py +0 -0
  114. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_app_shutdown.py +0 -0
  115. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_app_usage_line.py +0 -0
  116. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_btw_slash_command.py +0 -0
  117. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_cli_project_root.py +0 -0
  118. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_compact_command_semantics.py +0 -0
  119. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_completion_context_activation.py +0 -0
  120. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_completion_status_panel.py +0 -0
  121. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_context_command.py +0 -0
  122. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_custom_slash_commands.py +0 -0
  123. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_discover_tab.py +0 -0
  124. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_errors_tab.py +0 -0
  125. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_event_renderer.py +0 -0
  126. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_event_renderer_boundary.py +0 -0
  127. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_event_renderer_e2e.py +0 -0
  128. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_event_renderer_log_boundary.py +0 -0
  129. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_event_renderer_log_queue.py +0 -0
  130. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_event_renderer_streaming.py +0 -0
  131. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_event_renderer_tool_fold.py +0 -0
  132. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_format_error.py +0 -0
  133. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_handle_error.py +0 -0
  134. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_history_printer.py +0 -0
  135. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_history_printer_log.py +0 -0
  136. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_history_printer_subtitle_position.py +0 -0
  137. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_history_printer_tool_fold.py +0 -0
  138. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_history_sync.py +0 -0
  139. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_history_sync_tool_fold.py +0 -0
  140. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_input_behavior.py +0 -0
  141. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_input_history.py +0 -0
  142. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_installed_tab.py +0 -0
  143. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_interrupt_exit_semantics.py +0 -0
  144. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_layout_coordinator.py +0 -0
  145. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_logging_adapter.py +0 -0
  146. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_logo.py +0 -0
  147. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_main_args.py +0 -0
  148. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_markdown_render.py +0 -0
  149. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_marketplaces_tab.py +0 -0
  150. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_mcp_cli.py +0 -0
  151. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_mcp_slash_command.py +0 -0
  152. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_mention_completer.py +0 -0
  153. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_path_context_hint.py +0 -0
  154. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_plugin_slash_commands.py +0 -0
  155. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_plugin_tui_components.py +0 -0
  156. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_preflight.py +0 -0
  157. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_preflight_copilot.py +0 -0
  158. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_question_key_bindings.py +0 -0
  159. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_question_view.py +0 -0
  160. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_resume_picker.py +0 -0
  161. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_resume_preview.py +0 -0
  162. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_resume_selector.py +0 -0
  163. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_rewind_command_semantics.py +0 -0
  164. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_rpc_protocol.py +0 -0
  165. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_rpc_stdio_bridge.py +0 -0
  166. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_selection_menu.py +0 -0
  167. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_session_query_token_summary.py +0 -0
  168. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_shutdown_noise_guard.py +0 -0
  169. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_shutdown_noise_integration.py +0 -0
  170. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_skills_slash_command.py +0 -0
  171. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_slash_argument_hint.py +0 -0
  172. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_slash_clear.py +0 -0
  173. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_slash_completer.py +0 -0
  174. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_slash_registry.py +0 -0
  175. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_status_bar.py +0 -0
  176. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_status_bar_transient.py +0 -0
  177. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_task_panel_format.py +0 -0
  178. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_task_panel_key_bindings.py +0 -0
  179. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_task_panel_rendering.py +0 -0
  180. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_task_poll.py +0 -0
  181. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_tool_fold.py +0 -0
  182. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_tool_fold_panel.py +0 -0
  183. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_tool_result_formatters.py +0 -0
  184. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_tool_result_store.py +0 -0
  185. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_tool_result_viewer.py +0 -0
  186. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_tool_result_viewer_key_bindings.py +0 -0
  187. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_tool_view.py +0 -0
  188. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_transcript_viewer.py +0 -0
  189. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_transcript_viewer_tool_fold.py +0 -0
  190. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_tui_elapsed_status.py +0 -0
  191. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_tui_esc_queue.py +0 -0
  192. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_tui_mcp_init_gate.py +0 -0
  193. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_tui_paste_newline_guard.py +0 -0
  194. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_tui_paste_placeholder.py +0 -0
  195. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_tui_queue_preview.py +0 -0
  196. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_tui_queue_sdk_source.py +0 -0
  197. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_tui_split_invariance.py +0 -0
  198. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_tui_team_messages.py +0 -0
  199. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_tui_thinking_display.py +0 -0
  200. {comate_cli-0.7.1 → comate_cli-0.7.2}/tests/test_tui_tool_result_registry_lifecycle.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: comate-cli
3
- Version: 0.7.1
3
+ Version: 0.7.2
4
4
  Summary: Comate terminal CLI built on comate-agent-sdk
5
5
  Project-URL: Homepage, https://github.com/AndyLee1024/agent-sdk
6
6
  Project-URL: Repository, https://github.com/AndyLee1024/agent-sdk
@@ -1,8 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import asyncio
4
- import importlib.metadata
5
- import locale
6
4
  import logging
7
5
  import os
8
6
  import random
@@ -27,6 +25,18 @@ from comate_cli.terminal_agent.resume_selector import select_resume_session_id
27
25
  from comate_cli.terminal_agent.rpc_stdio import StdioRPCBridge
28
26
  from comate_cli.terminal_agent.status_bar import StatusBar
29
27
  from comate_cli.terminal_agent.tui import TerminalAgentTUI
28
+ from comate_cli.terminal_agent.update_check import (
29
+ UpdateInfo,
30
+ UpdatePromptChoice,
31
+ UpdatePromptDecision,
32
+ check_update,
33
+ decide_update_prompt,
34
+ format_update_hint,
35
+ mark_update_seen,
36
+ record_skip_until_next_version,
37
+ run_update_command,
38
+ show_update_prompt,
39
+ )
30
40
 
31
41
  console = Console()
32
42
  logger = logging.getLogger(__name__)
@@ -37,70 +47,32 @@ def _resolve_cli_project_root() -> Path:
37
47
  return Path.cwd().expanduser().resolve()
38
48
 
39
49
 
40
- def _is_chinese_locale() -> bool:
41
- """判断当前系统 locale 是否为中文环境。"""
42
- # zh_tw(台湾)和 zh_hk(香港)不走清华镜像源
43
- _CN_PREFIXES = ("zh_cn", "zh_sg", "zh_mo", "zh_hans")
44
- for env_var in ("LC_ALL", "LC_MESSAGES", "LANG", "LANGUAGE"):
45
- val = os.environ.get(env_var, "").lower()
46
- if any(val.startswith(p) for p in _CN_PREFIXES):
47
- return True
48
- try:
49
- lang, _ = locale.getlocale()
50
- if lang:
51
- lang_lower = lang.lower()
52
- if any(lang_lower.startswith(p) for p in _CN_PREFIXES):
53
- return True
54
- except Exception:
55
- pass
56
- return False
50
+ async def _check_update() -> UpdateInfo | None:
51
+ return await check_update(log=logger)
57
52
 
58
53
 
59
- async def _check_update() -> str | None:
60
- """异步检查 comate-cli 是否有新版本,返回提示字符串或 None。
54
+ async def _handle_update_on_launch(info: UpdateInfo) -> bool:
55
+ """Handle interactive startup update UX.
61
56
 
62
- 中文 locale 自动使用清华 TUNA 镜像源,其余使用官方 PyPI。
57
+ Returns True when launch should stop after a successful update.
63
58
  """
64
- package = "comate-cli"
65
59
  try:
66
- current = importlib.metadata.version(package)
67
- except importlib.metadata.PackageNotFoundError:
68
- return None
69
-
70
- if _is_chinese_locale():
71
- url = f"https://mirrors.tuna.tsinghua.edu.cn/pypi/{package}/json"
72
- source_label = "tuna"
73
- else:
74
- url = f"https://pypi.org/pypi/{package}/json"
75
- source_label = "pypi"
76
-
77
- try:
78
- import httpx
79
- async with httpx.AsyncClient(timeout=3.0) as client:
80
- resp = await client.get(url)
81
- resp.raise_for_status()
82
- latest = resp.json()["info"]["version"]
60
+ decision = decide_update_prompt(info)
61
+ if decision == UpdatePromptDecision.SKIP:
62
+ return False
63
+ if decision == UpdatePromptDecision.SHOW_HINT:
64
+ console.print(format_update_hint(info))
65
+ mark_update_seen(info)
66
+ return False
67
+
68
+ choice = await show_update_prompt(info)
69
+ if choice == UpdatePromptChoice.UPDATE_NOW:
70
+ return await run_update_command(console, info)
71
+ if choice == UpdatePromptChoice.SKIP_UNTIL_NEXT_VERSION:
72
+ record_skip_until_next_version(info)
83
73
  except Exception:
84
- logger.debug(f"update check failed (source={source_label})", exc_info=True)
85
- return None
86
-
87
- try:
88
- from packaging.version import Version
89
-
90
- if Version(latest) > Version(current):
91
- return (
92
- f"[dim] New version available: [bold cyan]{latest}[/] "
93
- f"(current: {current}) "
94
- f"Run [bold green]uv tool upgrade {package}[/] to upgrade.[/]"
95
- )
96
- except Exception:
97
- logger.debug(
98
- "update comparison failed (current=%s, latest=%s)",
99
- current,
100
- latest,
101
- exc_info=True,
102
- )
103
- return None
74
+ logger.debug("startup update prompt failed", exc_info=True)
75
+ return False
104
76
 
105
77
 
106
78
  def _flush_langfuse_if_configured() -> None:
@@ -366,6 +338,14 @@ async def run(
366
338
 
367
339
  print_logo(console, project_root=project_root)
368
340
 
341
+ # 版本检查:带超时,不阻塞启动;仅交互启动路径触发升级 UX。
342
+ try:
343
+ update_info = await asyncio.wait_for(_check_update(), timeout=2.0)
344
+ if update_info is not None and await _handle_update_on_launch(update_info):
345
+ return
346
+ except (asyncio.TimeoutError, Exception):
347
+ pass
348
+
369
349
  if resume_select and not resume_session_id:
370
350
  selected_session_id = await select_resume_session_id(console, cwd=project_root)
371
351
  if not selected_session_id:
@@ -384,14 +364,6 @@ async def run(
384
364
  # 这些日志没有交互 anchor,需在恢复历史或首次用户输入前落为 standalone log。
385
365
  renderer.flush_pending_logs()
386
366
 
387
- # 版本检查:带超时,不阻塞启动
388
- try:
389
- update_hint = await asyncio.wait_for(_check_update(), timeout=2.0)
390
- if update_hint:
391
- console.print(update_hint)
392
- except (asyncio.TimeoutError, Exception):
393
- pass
394
-
395
367
  status_bar = StatusBar(session)
396
368
  status_bar.set_mode(session.get_mode())
397
369
  try:
@@ -0,0 +1,334 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ import importlib.metadata
5
+ import locale
6
+ import logging
7
+ import os
8
+ from dataclasses import dataclass
9
+ from enum import Enum
10
+ from pathlib import Path
11
+ from typing import Any
12
+
13
+ from rich.console import Console
14
+
15
+ from comate_agent_sdk.agent.settings import (
16
+ USER_SETTINGS_PATH,
17
+ read_settings_json_object,
18
+ write_settings_json_atomic,
19
+ )
20
+
21
+ logger = logging.getLogger(__name__)
22
+
23
+ PACKAGE_NAME = "comate-cli"
24
+ RELEASE_NOTES_URL = "https://github.com/AndyLee1024/agent-sdk/releases/latest"
25
+ UPDATE_COMMAND = ("uv", "tool", "update", PACKAGE_NAME)
26
+ _SETTINGS_SECTION = "updates"
27
+
28
+
29
+ class UpdatePromptDecision(Enum):
30
+ SKIP = "skip"
31
+ SHOW_HINT = "show_hint"
32
+ SHOW_PROMPT = "show_prompt"
33
+
34
+
35
+ class UpdatePromptChoice(Enum):
36
+ UPDATE_NOW = "update_now"
37
+ SKIP = "skip"
38
+ SKIP_UNTIL_NEXT_VERSION = "skip_until_next_version"
39
+
40
+
41
+ @dataclass(frozen=True, slots=True)
42
+ class UpdateInfo:
43
+ package: str
44
+ current_version: str
45
+ latest_version: str
46
+ release_notes_url: str
47
+
48
+
49
+ def _is_chinese_locale() -> bool:
50
+ """判断当前系统 locale 是否为中文环境。"""
51
+ # zh_tw(台湾)和 zh_hk(香港)不走清华镜像源
52
+ cn_prefixes = ("zh_cn", "zh_sg", "zh_mo", "zh_hans")
53
+ for env_var in ("LC_ALL", "LC_MESSAGES", "LANG", "LANGUAGE"):
54
+ val = os.environ.get(env_var, "").lower()
55
+ if any(val.startswith(prefix) for prefix in cn_prefixes):
56
+ return True
57
+ try:
58
+ lang, _ = locale.getlocale()
59
+ if lang:
60
+ lang_lower = lang.lower()
61
+ if any(lang_lower.startswith(prefix) for prefix in cn_prefixes):
62
+ return True
63
+ except Exception:
64
+ pass
65
+ return False
66
+
67
+
68
+ async def check_update(
69
+ *,
70
+ package: str = PACKAGE_NAME,
71
+ release_notes_url: str = RELEASE_NOTES_URL,
72
+ log: logging.Logger | None = None,
73
+ ) -> UpdateInfo | None:
74
+ """异步检查 comate-cli 是否有新版本,返回结构化版本信息或 None。"""
75
+ active_logger = log or logger
76
+ try:
77
+ current = importlib.metadata.version(package)
78
+ except importlib.metadata.PackageNotFoundError:
79
+ return None
80
+
81
+ if _is_chinese_locale():
82
+ url = f"https://mirrors.tuna.tsinghua.edu.cn/pypi/{package}/json"
83
+ source_label = "tuna"
84
+ else:
85
+ url = f"https://pypi.org/pypi/{package}/json"
86
+ source_label = "pypi"
87
+
88
+ try:
89
+ import httpx
90
+
91
+ async with httpx.AsyncClient(timeout=3.0) as client:
92
+ resp = await client.get(url)
93
+ resp.raise_for_status()
94
+ latest = resp.json()["info"]["version"]
95
+ except Exception:
96
+ active_logger.debug(f"update check failed (source={source_label})", exc_info=True)
97
+ return None
98
+
99
+ try:
100
+ from packaging.version import Version
101
+
102
+ if Version(latest) > Version(current):
103
+ return UpdateInfo(
104
+ package=package,
105
+ current_version=current,
106
+ latest_version=latest,
107
+ release_notes_url=release_notes_url,
108
+ )
109
+ except Exception:
110
+ active_logger.debug(
111
+ "update comparison failed (current=%s, latest=%s)",
112
+ current,
113
+ latest,
114
+ exc_info=True,
115
+ )
116
+ return None
117
+
118
+
119
+ def format_update_hint(info: UpdateInfo) -> str:
120
+ return (
121
+ f"[dim] New version available: [bold cyan]{info.latest_version}[/] "
122
+ f"(current: {info.current_version}) "
123
+ f"Run [bold green]uv tool update {info.package}[/] to update.[/]"
124
+ )
125
+
126
+
127
+ def decide_update_prompt(
128
+ info: UpdateInfo,
129
+ *,
130
+ settings_path: Path | None = None,
131
+ ) -> UpdatePromptDecision:
132
+ state = _load_package_update_state(info.package, settings_path=settings_path)
133
+ if state.get("skip_until_version") == info.latest_version:
134
+ return UpdatePromptDecision.SKIP
135
+ if state.get("last_seen_version") == info.latest_version:
136
+ return UpdatePromptDecision.SHOW_PROMPT
137
+ return UpdatePromptDecision.SHOW_HINT
138
+
139
+
140
+ def mark_update_seen(info: UpdateInfo, *, settings_path: Path | None = None) -> None:
141
+ _write_package_update_state(
142
+ info.package,
143
+ {"last_seen_version": info.latest_version},
144
+ settings_path=settings_path,
145
+ )
146
+
147
+
148
+ def record_skip_until_next_version(
149
+ info: UpdateInfo,
150
+ *,
151
+ settings_path: Path | None = None,
152
+ ) -> None:
153
+ _write_package_update_state(
154
+ info.package,
155
+ {
156
+ "last_seen_version": info.latest_version,
157
+ "skip_until_version": info.latest_version,
158
+ },
159
+ settings_path=settings_path,
160
+ )
161
+
162
+
163
+ async def show_update_prompt(info: UpdateInfo) -> UpdatePromptChoice:
164
+ from prompt_toolkit.application import Application
165
+ from prompt_toolkit.key_binding import KeyBindings
166
+ from prompt_toolkit.layout import HSplit, Layout, Window
167
+ from prompt_toolkit.layout.controls import FormattedTextControl
168
+ from prompt_toolkit.patch_stdout import patch_stdout
169
+ from prompt_toolkit.styles import Style as PTStyle
170
+
171
+ choices = (
172
+ UpdatePromptChoice.UPDATE_NOW,
173
+ UpdatePromptChoice.SKIP,
174
+ UpdatePromptChoice.SKIP_UNTIL_NEXT_VERSION,
175
+ )
176
+ selected_index = 0
177
+
178
+ def render() -> list[tuple[str, str]]:
179
+ rows = [
180
+ ("class:title", f"✨ Update available! {info.current_version} -> {info.latest_version}\n"),
181
+ ("", "\n"),
182
+ # ("class:dim", f" Release notes: {info.release_notes_url}\n"),
183
+ # ("", "\n"),
184
+ ]
185
+ labels = (
186
+ f"Update now (runs `{_format_update_command()}`)",
187
+ "Skip",
188
+ "Skip until next version",
189
+ )
190
+ for index, label in enumerate(labels):
191
+ marker = "›" if index == selected_index else " "
192
+ style = "class:selected" if index == selected_index else ""
193
+ rows.append((style, f"{marker} {index + 1}. {label}\n"))
194
+ rows.extend(
195
+ [
196
+ ("", "\n"),
197
+ ("class:dim", " Press enter to continue"),
198
+ ]
199
+ )
200
+ return rows
201
+
202
+ kb = KeyBindings()
203
+
204
+ def exit_with(index: int, event: Any) -> None:
205
+ event.app.exit(result=choices[index])
206
+
207
+ @kb.add("up")
208
+ def _on_up(event: Any) -> None:
209
+ nonlocal selected_index
210
+ del event
211
+ selected_index = max(0, selected_index - 1)
212
+
213
+ @kb.add("down")
214
+ def _on_down(event: Any) -> None:
215
+ nonlocal selected_index
216
+ del event
217
+ selected_index = min(len(choices) - 1, selected_index + 1)
218
+
219
+ @kb.add("1")
220
+ def _on_one(event: Any) -> None:
221
+ exit_with(0, event)
222
+
223
+ @kb.add("2")
224
+ def _on_two(event: Any) -> None:
225
+ exit_with(1, event)
226
+
227
+ @kb.add("3")
228
+ def _on_three(event: Any) -> None:
229
+ exit_with(2, event)
230
+
231
+ @kb.add("enter")
232
+ def _on_enter(event: Any) -> None:
233
+ exit_with(selected_index, event)
234
+
235
+ @kb.add("escape")
236
+ @kb.add("c-c")
237
+ def _on_cancel(event: Any) -> None:
238
+ event.app.exit(result=UpdatePromptChoice.SKIP)
239
+
240
+ app: Application[UpdatePromptChoice] = Application(
241
+ layout=Layout(
242
+ HSplit(
243
+ [
244
+ Window(
245
+ content=FormattedTextControl(text=render, focusable=True),
246
+ wrap_lines=False,
247
+ )
248
+ ]
249
+ )
250
+ ),
251
+ key_bindings=kb,
252
+ style=PTStyle.from_dict(
253
+ {
254
+ "title": "bold",
255
+ "dim": "ansibrightblack",
256
+ "selected": "bold",
257
+ }
258
+ ),
259
+ full_screen=True,
260
+ erase_when_done=True,
261
+ mouse_support=False,
262
+ )
263
+ with patch_stdout(raw=True):
264
+ return await app.run_async()
265
+
266
+
267
+ async def run_update_command(console: Console, info: UpdateInfo) -> bool:
268
+ command = ("uv", "tool", "update", info.package)
269
+ command_text = " ".join(command)
270
+ console.print(f"Updating Comate via `{command_text}`...")
271
+ try:
272
+ process = await asyncio.create_subprocess_exec(
273
+ *command,
274
+ stdout=asyncio.subprocess.PIPE,
275
+ stderr=asyncio.subprocess.STDOUT,
276
+ )
277
+ except OSError as exc:
278
+ logger.warning("failed to start update command: %s", exc, exc_info=True)
279
+ console.print(f"[red]Failed to start `{command_text}`: {exc}[/]")
280
+ return False
281
+
282
+ output, _ = await process.communicate()
283
+ if output:
284
+ console.out(output.decode(errors="replace"), end="")
285
+
286
+ if process.returncode == 0:
287
+ console.print("🎉 Update ran successfully! Please restart Comate.")
288
+ return True
289
+
290
+ console.print(f"[red]Update failed with exit code {process.returncode}. Continuing with current Comate.[/]")
291
+ return False
292
+
293
+
294
+ def _load_package_update_state(
295
+ package: str,
296
+ *,
297
+ settings_path: Path | None,
298
+ ) -> dict[str, Any]:
299
+ data = read_settings_json_object(settings_path or USER_SETTINGS_PATH)
300
+ updates = data.get(_SETTINGS_SECTION)
301
+ if not isinstance(updates, dict):
302
+ return {}
303
+ state = updates.get(_package_state_key(package))
304
+ if not isinstance(state, dict):
305
+ return {}
306
+ return dict(state)
307
+
308
+
309
+ def _write_package_update_state(
310
+ package: str,
311
+ values: dict[str, str],
312
+ *,
313
+ settings_path: Path | None,
314
+ ) -> None:
315
+ path = settings_path or USER_SETTINGS_PATH
316
+ data = read_settings_json_object(path)
317
+ updates = data.get(_SETTINGS_SECTION)
318
+ if not isinstance(updates, dict):
319
+ updates = {}
320
+ state = updates.get(_package_state_key(package))
321
+ if not isinstance(state, dict):
322
+ state = {}
323
+ state.update(values)
324
+ updates[_package_state_key(package)] = state
325
+ data[_SETTINGS_SECTION] = updates
326
+ write_settings_json_atomic(path=path, data=data)
327
+
328
+
329
+ def _package_state_key(package: str) -> str:
330
+ return package.replace("-", "_")
331
+
332
+
333
+ def _format_update_command() -> str:
334
+ return " ".join(UPDATE_COMMAND)
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "comate-cli"
7
- version = "0.7.1"
7
+ version = "0.7.2"
8
8
  description = "Comate terminal CLI built on comate-agent-sdk"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -42,6 +42,12 @@ comate = "comate_cli.main:main"
42
42
 
43
43
  [tool.hatch.build.targets.wheel]
44
44
  packages = ["comate_cli"]
45
+ # hatchling>=1.30 默认输出 core metadata 2.5 (PEP 794),但 PyPI warehouse
46
+ # 暂未接受 metadata 2.5,上传会被拒。先固定到 2.4,等 warehouse 支持后再切回。
47
+ core-metadata-version = "2.4"
48
+
49
+ [tool.hatch.build.targets.sdist]
50
+ core-metadata-version = "2.4"
45
51
 
46
52
  [tool.pytest.ini_options]
47
53
  asyncio_mode = "auto"