comate-cli 0.7.7__tar.gz → 0.7.9__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 (206) hide show
  1. {comate_cli-0.7.7 → comate_cli-0.7.9}/PKG-INFO +1 -1
  2. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/main.py +113 -11
  3. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/app.py +233 -57
  4. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/event_renderer.py +4 -2
  5. comate_cli-0.7.9/comate_cli/terminal_agent/preflight.py +210 -0
  6. comate_cli-0.7.7/comate_cli/terminal_agent/preflight.py → comate_cli-0.7.9/comate_cli/terminal_agent/preflight_wizard.py +14 -202
  7. comate_cli-0.7.9/comate_cli/terminal_agent/print_mode.py +224 -0
  8. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/status_bar.py +3 -5
  9. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/statusline/model.py +1 -5
  10. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/statusline/picker_state.py +0 -1
  11. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/tool_result_formatters.py +156 -28
  12. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/tui_parts/history_sync.py +48 -10
  13. {comate_cli-0.7.7 → comate_cli-0.7.9}/pyproject.toml +1 -1
  14. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/statusline/test_model.py +3 -5
  15. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/statusline/test_picker_state.py +3 -4
  16. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/statusline/test_store.py +17 -0
  17. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_app_preflight_gate.py +11 -9
  18. comate_cli-0.7.9/tests/test_app_print_mode.py +375 -0
  19. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_app_shutdown.py +20 -7
  20. comate_cli-0.7.9/tests/test_app_startup_latency.py +578 -0
  21. comate_cli-0.7.9/tests/test_app_token_cost_config.py +164 -0
  22. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_event_renderer.py +63 -3
  23. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_history_sync.py +185 -3
  24. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_main_args.py +122 -11
  25. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_preflight.py +51 -6
  26. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_preflight_copilot.py +1 -1
  27. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_resume_picker.py +5 -0
  28. comate_cli-0.7.9/tests/test_startup_import_budget.py +118 -0
  29. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_startup_profile.py +75 -11
  30. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_status_bar.py +17 -2
  31. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_tool_result_formatters.py +178 -0
  32. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_update_check.py +106 -48
  33. {comate_cli-0.7.7 → comate_cli-0.7.9}/uv.lock +2 -2
  34. comate_cli-0.7.7/tests/test_app_print_mode.py +0 -90
  35. comate_cli-0.7.7/tests/test_app_startup_latency.py +0 -304
  36. comate_cli-0.7.7/tests/test_app_token_cost_config.py +0 -77
  37. {comate_cli-0.7.7 → comate_cli-0.7.9}/.gitignore +0 -0
  38. {comate_cli-0.7.7 → comate_cli-0.7.9}/CHANGELOG.md +0 -0
  39. {comate_cli-0.7.7 → comate_cli-0.7.9}/README.md +0 -0
  40. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/__init__.py +0 -0
  41. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/__main__.py +0 -0
  42. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/mcp_cli.py +0 -0
  43. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/__init__.py +0 -0
  44. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/animations.py +0 -0
  45. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/assistant_render.py +0 -0
  46. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/codenames.py +0 -0
  47. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/config/__init__.py +0 -0
  48. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/config/model.py +0 -0
  49. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/config/picker.py +0 -0
  50. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/config/picker_state.py +0 -0
  51. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/config/store.py +0 -0
  52. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/custom_slash_commands.py +0 -0
  53. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/env_utils.py +0 -0
  54. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/error_display.py +0 -0
  55. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/figures.py +0 -0
  56. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/fragment_utils.py +0 -0
  57. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/goal_resume_view.py +0 -0
  58. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/history_printer.py +0 -0
  59. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/input_geometry.py +0 -0
  60. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/layout_coordinator.py +0 -0
  61. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/logging_adapter.py +0 -0
  62. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/logo.py +0 -0
  63. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/markdown_render.py +0 -0
  64. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/mention_completer.py +0 -0
  65. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/message_style.py +0 -0
  66. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/models.py +0 -0
  67. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/path_context_hint.py +0 -0
  68. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/plugins/__init__.py +0 -0
  69. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/plugins/components/__init__.py +0 -0
  70. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/plugins/components/detail_view.py +0 -0
  71. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/plugins/components/plugin_list.py +0 -0
  72. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/plugins/components/search_box.py +0 -0
  73. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/plugins/components/tab_bar.py +0 -0
  74. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/plugins/marketplace_install_view.py +0 -0
  75. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/plugins/plugin_picker.py +0 -0
  76. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/plugins/tabs/__init__.py +0 -0
  77. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/plugins/tabs/discover_tab.py +0 -0
  78. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/plugins/tabs/errors_tab.py +0 -0
  79. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/plugins/tabs/installed_tab.py +0 -0
  80. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/plugins/tabs/marketplaces_tab.py +0 -0
  81. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/question_view.py +0 -0
  82. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/resume_picker.py +0 -0
  83. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/resume_preview.py +0 -0
  84. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/resume_selector.py +0 -0
  85. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/rpc_protocol.py +0 -0
  86. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/rpc_stdio.py +0 -0
  87. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/selection_menu.py +0 -0
  88. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/slash_commands.py +0 -0
  89. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/startup.py +0 -0
  90. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/startup_profile.py +0 -0
  91. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/statusline/__init__.py +0 -0
  92. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/statusline/picker.py +0 -0
  93. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/statusline/store.py +0 -0
  94. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/text_effects.py +0 -0
  95. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/tips.py +0 -0
  96. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/tool_fold.py +0 -0
  97. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/tool_result_store.py +0 -0
  98. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/tool_result_viewer.py +0 -0
  99. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/tool_view.py +0 -0
  100. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/transcript_viewer.py +0 -0
  101. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/tui.py +0 -0
  102. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/tui_parts/__init__.py +0 -0
  103. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/tui_parts/btw_view.py +0 -0
  104. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/tui_parts/commands.py +0 -0
  105. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/tui_parts/input_behavior.py +0 -0
  106. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/tui_parts/key_bindings.py +0 -0
  107. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/tui_parts/mcp_connecting_view.py +0 -0
  108. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/tui_parts/render_panels.py +0 -0
  109. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/tui_parts/slash_command_registry.py +0 -0
  110. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/tui_parts/ui_mode.py +0 -0
  111. {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/update_check.py +0 -0
  112. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/config/__init__.py +0 -0
  113. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/config/test_model.py +0 -0
  114. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/config/test_picker_state.py +0 -0
  115. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/config/test_picker_ui.py +0 -0
  116. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/config/test_roundtrip.py +0 -0
  117. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/config/test_store_load.py +0 -0
  118. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/config/test_store_save.py +0 -0
  119. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/conftest.py +0 -0
  120. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/fixtures/fake_mcp_misbehaving_stdout.py +0 -0
  121. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/statusline/__init__.py +0 -0
  122. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_animator_shuffle.py +0 -0
  123. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_app_mcp_preload.py +0 -0
  124. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_app_usage_line.py +0 -0
  125. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_btw_slash_command.py +0 -0
  126. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_cli_project_root.py +0 -0
  127. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_compact_command_semantics.py +0 -0
  128. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_completion_context_activation.py +0 -0
  129. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_completion_status_panel.py +0 -0
  130. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_context_command.py +0 -0
  131. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_custom_slash_commands.py +0 -0
  132. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_discover_tab.py +0 -0
  133. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_errors_tab.py +0 -0
  134. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_event_renderer_boundary.py +0 -0
  135. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_event_renderer_e2e.py +0 -0
  136. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_event_renderer_log_boundary.py +0 -0
  137. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_event_renderer_log_queue.py +0 -0
  138. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_event_renderer_streaming.py +0 -0
  139. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_event_renderer_tool_fold.py +0 -0
  140. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_format_error.py +0 -0
  141. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_goal_resume_tui.py +0 -0
  142. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_goal_resume_view.py +0 -0
  143. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_goal_slash_command.py +0 -0
  144. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_handle_error.py +0 -0
  145. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_history_printer.py +0 -0
  146. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_history_printer_log.py +0 -0
  147. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_history_printer_subtitle_position.py +0 -0
  148. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_history_printer_tool_fold.py +0 -0
  149. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_history_sync_tool_fold.py +0 -0
  150. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_input_behavior.py +0 -0
  151. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_input_history.py +0 -0
  152. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_installed_tab.py +0 -0
  153. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_interrupt_exit_semantics.py +0 -0
  154. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_layout_coordinator.py +0 -0
  155. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_logging_adapter.py +0 -0
  156. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_logo.py +0 -0
  157. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_markdown_render.py +0 -0
  158. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_marketplaces_tab.py +0 -0
  159. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_mcp_cli.py +0 -0
  160. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_mcp_slash_command.py +0 -0
  161. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_mention_completer.py +0 -0
  162. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_path_context_hint.py +0 -0
  163. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_plugin_slash_commands.py +0 -0
  164. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_plugin_tui_components.py +0 -0
  165. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_question_key_bindings.py +0 -0
  166. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_question_view.py +0 -0
  167. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_resume_preview.py +0 -0
  168. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_resume_selector.py +0 -0
  169. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_rewind_command_semantics.py +0 -0
  170. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_rpc_protocol.py +0 -0
  171. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_rpc_stdio_bridge.py +0 -0
  172. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_selection_menu.py +0 -0
  173. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_session_query_token_summary.py +0 -0
  174. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_shutdown_noise_guard.py +0 -0
  175. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_shutdown_noise_integration.py +0 -0
  176. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_skills_slash_command.py +0 -0
  177. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_slash_argument_hint.py +0 -0
  178. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_slash_clear.py +0 -0
  179. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_slash_completer.py +0 -0
  180. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_slash_registry.py +0 -0
  181. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_status_bar_transient.py +0 -0
  182. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_task_panel_format.py +0 -0
  183. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_task_panel_key_bindings.py +0 -0
  184. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_task_panel_rendering.py +0 -0
  185. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_task_poll.py +0 -0
  186. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_tool_fold.py +0 -0
  187. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_tool_fold_panel.py +0 -0
  188. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_tool_result_store.py +0 -0
  189. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_tool_result_viewer.py +0 -0
  190. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_tool_result_viewer_key_bindings.py +0 -0
  191. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_tool_view.py +0 -0
  192. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_transcript_viewer.py +0 -0
  193. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_transcript_viewer_tool_fold.py +0 -0
  194. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_tui_elapsed_status.py +0 -0
  195. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_tui_esc_queue.py +0 -0
  196. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_tui_mcp_init_gate.py +0 -0
  197. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_tui_paste_newline_guard.py +0 -0
  198. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_tui_paste_placeholder.py +0 -0
  199. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_tui_queue_preview.py +0 -0
  200. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_tui_queue_sdk_source.py +0 -0
  201. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_tui_split_invariance.py +0 -0
  202. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_tui_startup_latency.py +0 -0
  203. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_tui_team_messages.py +0 -0
  204. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_tui_thinking_display.py +0 -0
  205. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_tui_tool_result_registry_lifecycle.py +0 -0
  206. {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_usage_command.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: comate-cli
3
- Version: 0.7.7
3
+ Version: 0.7.9
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
@@ -26,6 +26,15 @@ class _ArgumentError(ValueError):
26
26
  """Raised when CLI arguments are invalid."""
27
27
 
28
28
 
29
+ PRINT_STDIN_IDLE_TIMEOUT_SECONDS = 3.0
30
+ PRINT_OUTPUT_FORMAT_TEXT = "text"
31
+ PRINT_OUTPUT_FORMAT_JSON = "json"
32
+ PRINT_OUTPUT_FORMATS = frozenset({PRINT_OUTPUT_FORMAT_TEXT, PRINT_OUTPUT_FORMAT_JSON})
33
+ PRINT_OUTPUT_FORMAT_UNSUPPORTED = frozenset({"stream-json"})
34
+
35
+ ParsedArgs = tuple[bool, str | None, bool, str | None, str]
36
+
37
+
29
38
  class _TerminalStateGuard:
30
39
  """Best-effort tty restore guard for abnormal shutdown paths."""
31
40
 
@@ -200,37 +209,68 @@ def _usage_text() -> str:
200
209
  return (
201
210
  "Usage:\n"
202
211
  " comate [--rpc-stdio]\n"
203
- " comate -p <prompt>\n"
212
+ " comate -p [--output-format text|json] <prompt>\n"
204
213
  " comate resume [<session_id>] [--rpc-stdio]\n"
205
214
  " comate mcp <subcommand> [options]"
206
215
  )
207
216
 
208
217
 
209
- def _parse_args(argv: list[str]) -> tuple[bool, str | None, bool, str | None]:
218
+ def _parse_args(argv: list[str]) -> ParsedArgs:
210
219
  rpc_stdio = False
211
220
  print_mode = False
221
+ output_format = PRINT_OUTPUT_FORMAT_TEXT
222
+ output_format_explicit = False
212
223
  positionals: list[str] = []
213
- for arg in argv:
224
+
225
+ index = 0
226
+ while index < len(argv):
227
+ arg = argv[index]
214
228
  if arg == "--rpc-stdio":
215
229
  rpc_stdio = True
230
+ index += 1
216
231
  continue
217
232
  if arg in ("-p", "--print"):
218
233
  print_mode = True
234
+ index += 1
235
+ continue
236
+ if arg == "--output-format" or arg.startswith("--output-format="):
237
+ output_format_explicit = True
238
+ if arg == "--output-format":
239
+ index += 1
240
+ if index >= len(argv):
241
+ raise _ArgumentError("--output-format requires one of: text, json")
242
+ value = argv[index]
243
+ else:
244
+ value = arg.partition("=")[2]
245
+ if not value:
246
+ raise _ArgumentError("--output-format requires one of: text, json")
247
+ if value in PRINT_OUTPUT_FORMAT_UNSUPPORTED:
248
+ raise _ArgumentError(
249
+ "--output-format stream-json is not supported in this release"
250
+ )
251
+ if value not in PRINT_OUTPUT_FORMATS:
252
+ raise _ArgumentError(f"Unsupported --output-format: {value}")
253
+ output_format = value
254
+ index += 1
219
255
  continue
220
256
  if arg.startswith("-"):
221
257
  raise _ArgumentError(f"Unknown option: {arg}")
222
258
  positionals.append(arg)
259
+ index += 1
260
+
261
+ if output_format_explicit and not print_mode:
262
+ raise _ArgumentError("--output-format can only be used with -p/--print")
223
263
 
224
264
  # -p mode: all positionals become the prompt
225
265
  if print_mode:
226
266
  if rpc_stdio:
227
267
  raise _ArgumentError("-p/--print and --rpc-stdio are mutually exclusive")
228
268
  print_prompt = " ".join(positionals) if positionals else ""
229
- return rpc_stdio, None, False, print_prompt
269
+ return rpc_stdio, None, False, print_prompt, output_format
230
270
 
231
271
  # Non -p mode: original logic
232
272
  if not positionals:
233
- return rpc_stdio, None, False, None
273
+ return rpc_stdio, None, False, None, output_format
234
274
 
235
275
  command = positionals[0]
236
276
  if command != "resume":
@@ -241,14 +281,75 @@ def _parse_args(argv: list[str]) -> tuple[bool, str | None, bool, str | None]:
241
281
  raise _ArgumentError(
242
282
  "resume without <session_id> does not support --rpc-stdio"
243
283
  )
244
- return rpc_stdio, None, True, None
284
+ return rpc_stdio, None, True, None, output_format
245
285
 
246
286
  if len(positionals) == 2:
247
- return rpc_stdio, positionals[1], False, None
287
+ return rpc_stdio, positionals[1], False, None, output_format
248
288
 
249
289
  raise _ArgumentError("resume accepts at most one <session_id>")
250
290
 
251
291
 
292
+ def _read_stdin_posix_idle_drain(
293
+ stdin: object,
294
+ *,
295
+ timeout_seconds: float,
296
+ chunk_size: int = 64 * 1024,
297
+ ) -> str:
298
+ import select
299
+
300
+ fileno = getattr(stdin, "fileno", None)
301
+ if not callable(fileno):
302
+ read = getattr(stdin, "read", None)
303
+ return str(read()) if callable(read) else ""
304
+
305
+ try:
306
+ fd = fileno()
307
+ except (AttributeError, OSError, ValueError):
308
+ read = getattr(stdin, "read", None)
309
+ return str(read()) if callable(read) else ""
310
+
311
+ chunks: list[bytes] = []
312
+ while True:
313
+ readable, _, _ = select.select([fd], [], [], timeout_seconds)
314
+ if not readable:
315
+ break
316
+ try:
317
+ chunk = os.read(fd, chunk_size)
318
+ except BlockingIOError:
319
+ break
320
+ except OSError:
321
+ break
322
+ if not chunk:
323
+ break
324
+ chunks.append(chunk)
325
+
326
+ encoding = str(getattr(stdin, "encoding", "") or "utf-8")
327
+ return b"".join(chunks).decode(encoding, errors="replace")
328
+
329
+
330
+ def _read_stdin_if_ready(
331
+ stdin: object,
332
+ *,
333
+ timeout_seconds: float = PRINT_STDIN_IDLE_TIMEOUT_SECONDS,
334
+ ) -> str:
335
+ isatty = getattr(stdin, "isatty", None)
336
+ if callable(isatty) and isatty():
337
+ return ""
338
+
339
+ if os.name != "posix":
340
+ read = getattr(stdin, "read", None)
341
+ return str(read()) if callable(read) else ""
342
+
343
+ try:
344
+ return _read_stdin_posix_idle_drain(
345
+ stdin,
346
+ timeout_seconds=timeout_seconds,
347
+ )
348
+ except (OSError, ValueError):
349
+ read = getattr(stdin, "read", None)
350
+ return str(read()) if callable(read) else ""
351
+
352
+
252
353
  def main(argv: list[str] | None = None) -> None:
253
354
  run_argv = list(argv) if argv is not None else sys.argv[1:]
254
355
 
@@ -278,7 +379,9 @@ def main(argv: list[str] | None = None) -> None:
278
379
  _app_import_done_perf = time.perf_counter()
279
380
 
280
381
  try:
281
- rpc_stdio, resume_session_id, resume_select, print_prompt = _parse_args(run_argv)
382
+ rpc_stdio, resume_session_id, resume_select, print_prompt, print_output_format = (
383
+ _parse_args(run_argv)
384
+ )
282
385
  except _ArgumentError as exc:
283
386
  sys.stderr.write(f"{exc}\n{_usage_text()}\n")
284
387
  raise SystemExit(2) from exc
@@ -286,9 +389,7 @@ def main(argv: list[str] | None = None) -> None:
286
389
  # Assemble print mode message
287
390
  print_message: str | None = None
288
391
  if print_prompt is not None:
289
- stdin_text = ""
290
- if not sys.stdin.isatty():
291
- stdin_text = sys.stdin.read()
392
+ stdin_text = _read_stdin_if_ready(sys.stdin)
292
393
  parts = [p for p in (stdin_text.strip(), print_prompt.strip()) if p]
293
394
  if not parts:
294
395
  sys.stderr.write("Error: -p requires a prompt argument or stdin input\n")
@@ -302,6 +403,7 @@ def main(argv: list[str] | None = None) -> None:
302
403
  resume_session_id=resume_session_id,
303
404
  resume_select=resume_select,
304
405
  print_message=print_message,
406
+ print_output_format=print_output_format,
305
407
  process_start_perf=_PROCESS_START_PERF,
306
408
  app_import_start_perf=_app_import_start_perf,
307
409
  app_import_done_perf=_app_import_done_perf,
@@ -5,46 +5,44 @@ import logging
5
5
  import os
6
6
  import random # noqa: F401 - tests patch this module-level import for deterministic startup tips.
7
7
  import signal
8
- import sys
9
8
  import threading
10
9
  import time
11
10
  from collections.abc import Iterator
12
11
  from contextlib import contextmanager, suppress
13
12
  from pathlib import Path
13
+ from typing import TYPE_CHECKING
14
14
 
15
15
  from rich.console import Console
16
16
 
17
- from comate_agent_sdk.agent import Agent, AgentConfig, ChatSession, TextEvent
18
- from comate_agent_sdk.context import EnvOptions
19
- from comate_agent_sdk.tools import tool
20
-
21
- from comate_cli.terminal_agent.event_renderer import EventRenderer
22
- from comate_cli.terminal_agent.logo import print_logo
23
- from comate_cli.terminal_agent.preflight import run_preflight_if_needed
24
- from comate_cli.terminal_agent.resume_selector import select_resume_session_id
25
- from comate_cli.terminal_agent.rpc_stdio import StdioRPCBridge
26
17
  from comate_cli.terminal_agent.startup_profile import StartupProfiler
27
- from comate_cli.terminal_agent.status_bar import StatusBar
28
- from comate_cli.terminal_agent.tui import TerminalAgentTUI
29
- from comate_cli.terminal_agent.update_check import (
30
- UpdateInfo,
31
- UpdatePromptChoice,
32
- UpdatePromptDecision,
33
- check_update,
34
- decide_update_prompt,
35
- format_update_hint,
36
- get_pending_update_prompt_info,
37
- mark_update_seen,
38
- record_skip_until_next_version,
39
- record_update_check_attempt,
40
- run_update_command,
41
- should_check_for_update,
42
- show_update_prompt,
43
- )
18
+
19
+ if TYPE_CHECKING:
20
+ from comate_agent_sdk.agent.chat_session import ChatSession
21
+ from comate_agent_sdk.agent.core.template import AgentTemplate as Agent
22
+ from comate_cli.terminal_agent.event_renderer import EventRenderer
23
+ from comate_cli.terminal_agent.update_check import UpdateInfo
44
24
 
45
25
  console = Console()
46
26
  logger = logging.getLogger(__name__)
47
27
  _UPDATE_CHECK_DELAY_S = 1.0
28
+ PRINT_MODE_DISALLOWED_TOOLS = (
29
+ "TeamCreate",
30
+ "TeamDelete",
31
+ "SendMessage",
32
+ "YieldTurn",
33
+ )
34
+
35
+
36
+ def _merge_disallowed_tools(*groups: tuple[str, ...] | None) -> tuple[str, ...] | None:
37
+ merged: list[str] = []
38
+ seen: set[str] = set()
39
+ for group in groups:
40
+ for name in group or ():
41
+ if name in seen:
42
+ continue
43
+ seen.add(name)
44
+ merged.append(name)
45
+ return tuple(merged) or None
48
46
 
49
47
 
50
48
  def _resolve_cli_project_root() -> Path:
@@ -56,6 +54,7 @@ async def _check_update(*, profiler: StartupProfiler | None = None) -> UpdateInf
56
54
  if profiler is not None:
57
55
  profiler.mark("impl.start")
58
56
  try:
57
+ check_update, _, _, _ = _import_update_check_core()
59
58
  return await check_update(log=logger, profiler=profiler)
60
59
  finally:
61
60
  if profiler is not None:
@@ -67,6 +66,16 @@ async def _handle_update_on_launch(info: UpdateInfo) -> bool:
67
66
 
68
67
  Returns True when launch should stop after a successful update.
69
68
  """
69
+ (
70
+ UpdatePromptChoice,
71
+ UpdatePromptDecision,
72
+ decide_update_prompt,
73
+ format_update_hint,
74
+ mark_update_seen,
75
+ record_skip_until_next_version,
76
+ run_update_command,
77
+ show_update_prompt,
78
+ ) = _import_update_prompt_components()
70
79
  try:
71
80
  decision = decide_update_prompt(info)
72
81
  if decision == UpdatePromptDecision.SKIP:
@@ -92,6 +101,16 @@ async def _handle_background_update_on_launch(
92
101
  renderer: EventRenderer,
93
102
  profiler: StartupProfiler,
94
103
  ) -> None:
104
+ (
105
+ _UpdatePromptChoice,
106
+ UpdatePromptDecision,
107
+ decide_update_prompt,
108
+ format_update_hint,
109
+ mark_update_seen,
110
+ _record_skip_until_next_version,
111
+ _run_update_command,
112
+ _show_update_prompt,
113
+ ) = _import_update_prompt_components()
95
114
  try:
96
115
  decision = decide_update_prompt(info)
97
116
  if decision == UpdatePromptDecision.SKIP:
@@ -198,6 +217,133 @@ def _sigint_guard() -> Iterator[None]:
198
217
  logger.warning(f"Failed to restore SIGINT handler: {exc}", exc_info=True)
199
218
 
200
219
 
220
+ @contextmanager
221
+ def _profile_section(
222
+ profiler: StartupProfiler | None,
223
+ phase: str,
224
+ ) -> Iterator[None]:
225
+ if profiler is not None:
226
+ profiler.mark(f"{phase}.start")
227
+ try:
228
+ yield
229
+ finally:
230
+ if profiler is not None:
231
+ profiler.mark(f"{phase}.done")
232
+
233
+
234
+ def _import_preflight_runner():
235
+ from comate_cli.terminal_agent.preflight import run_preflight_if_needed
236
+
237
+ return run_preflight_if_needed
238
+
239
+
240
+ def _import_print_mode_runner():
241
+ from comate_cli.terminal_agent.print_mode import run_print_mode
242
+
243
+ return run_print_mode
244
+
245
+
246
+ def _import_print_mode_json_error_writer():
247
+ from comate_cli.terminal_agent.print_mode import write_json_error_result
248
+
249
+ return write_json_error_result
250
+
251
+
252
+ def print_logo(*args, **kwargs) -> None:
253
+ from comate_cli.terminal_agent.logo import print_logo as _print_logo
254
+
255
+ _print_logo(*args, **kwargs)
256
+
257
+
258
+ def _import_sdk_agent_components(
259
+ profiler: StartupProfiler | None = None,
260
+ ) -> tuple[type, type, type, type]:
261
+ with _profile_section(profiler, "main.import.sdk_agent"):
262
+ from comate_agent_sdk.agent.compaction import CompactionConfig
263
+ from comate_agent_sdk.agent.options import AgentConfig
264
+ from comate_agent_sdk.agent.service import Agent
265
+ from comate_agent_sdk.context import EnvOptions
266
+
267
+ return Agent, AgentConfig, CompactionConfig, EnvOptions
268
+
269
+
270
+ def _import_chat_session() -> type:
271
+ from comate_agent_sdk.agent.chat_session import ChatSession
272
+
273
+ return ChatSession
274
+
275
+
276
+ def _import_print_mode_components() -> tuple[type, type]:
277
+ from comate_agent_sdk.agent.chat_session import ChatSession
278
+ from comate_agent_sdk.agent.events import TextEvent
279
+
280
+ return ChatSession, TextEvent
281
+
282
+
283
+ def _import_rpc_bridge() -> type:
284
+ from comate_cli.terminal_agent.rpc_stdio import StdioRPCBridge
285
+
286
+ return StdioRPCBridge
287
+
288
+
289
+ def _import_resume_selector():
290
+ from comate_cli.terminal_agent.resume_selector import select_resume_session_id
291
+
292
+ return select_resume_session_id
293
+
294
+
295
+ def _import_interactive_tui_components(
296
+ profiler: StartupProfiler | None = None,
297
+ ):
298
+ with _profile_section(profiler, "main.import.tui"):
299
+ from comate_cli.terminal_agent.event_renderer import EventRenderer
300
+ from comate_cli.terminal_agent.logging_adapter import setup_tui_logging
301
+ from comate_cli.terminal_agent.status_bar import StatusBar
302
+ from comate_cli.terminal_agent.tui import TerminalAgentTUI
303
+
304
+ return EventRenderer, setup_tui_logging, StatusBar, TerminalAgentTUI
305
+
306
+
307
+ def _import_update_check_core():
308
+ from comate_cli.terminal_agent.update_check import (
309
+ check_update,
310
+ get_pending_update_prompt_info,
311
+ record_update_check_attempt,
312
+ should_check_for_update,
313
+ )
314
+
315
+ return (
316
+ check_update,
317
+ get_pending_update_prompt_info,
318
+ record_update_check_attempt,
319
+ should_check_for_update,
320
+ )
321
+
322
+
323
+ def _import_update_prompt_components():
324
+ from comate_cli.terminal_agent.update_check import (
325
+ UpdatePromptChoice,
326
+ UpdatePromptDecision,
327
+ decide_update_prompt,
328
+ format_update_hint,
329
+ mark_update_seen,
330
+ record_skip_until_next_version,
331
+ run_update_command,
332
+ show_update_prompt,
333
+ )
334
+
335
+ return (
336
+ UpdatePromptChoice,
337
+ UpdatePromptDecision,
338
+ decide_update_prompt,
339
+ format_update_hint,
340
+ mark_update_seen,
341
+ record_skip_until_next_version,
342
+ run_update_command,
343
+ show_update_prompt,
344
+ )
345
+
346
+
201
347
  async def _shutdown_session(session: ChatSession, *, label: str) -> None:
202
348
  start = time.monotonic()
203
349
  try:
@@ -235,17 +381,13 @@ async def _graceful_shutdown(*sessions: ChatSession) -> None:
235
381
  logger.info(f"Graceful shutdown completed in {elapsed:.3f}s")
236
382
 
237
383
 
238
- @tool("Add two numbers 涉及到加法运算 必须使用这个工具")
239
- async def add(a: int, b: int) -> int:
240
- return a + b
241
-
242
-
243
384
  def _build_agent(
244
385
  *,
245
386
  project_root: Path | None = None,
246
387
  profiler: StartupProfiler | None = None,
388
+ extra_disallowed_tools: tuple[str, ...] | None = None,
247
389
  ) -> Agent:
248
- from comate_agent_sdk.agent.compaction import CompactionConfig
390
+ Agent, AgentConfig, CompactionConfig, EnvOptions = _import_sdk_agent_components(profiler)
249
391
  from comate_cli.terminal_agent.config import store
250
392
 
251
393
  resolved_project_root = project_root or _resolve_cli_project_root()
@@ -254,6 +396,7 @@ def _build_agent(
254
396
  snapshot = store.load()
255
397
  if profiler is not None:
256
398
  profiler.mark("agent.config.load.done")
399
+ disallowed_tools = _merge_disallowed_tools(extra_disallowed_tools)
257
400
 
258
401
  agent_config = AgentConfig(
259
402
  role="software_engineering",
@@ -261,11 +404,13 @@ def _build_agent(
261
404
  env_options=EnvOptions(system_env=True, git_env=True),
262
405
  use_streaming_task=True,
263
406
  include_cost=bool(getattr(snapshot, "token_cost_enabled", False)),
407
+ strict_hook_settings=False,
264
408
  memory_background_enabled=snapshot.memory_background_enabled,
265
409
  memory_dreaming_enabled=snapshot.memory_dreaming_enabled,
266
410
  compaction=CompactionConfig(
267
411
  threshold_ratio=snapshot.compaction_threshold_ratio,
268
412
  ),
413
+ disallowed_tools=disallowed_tools,
269
414
  )
270
415
  if profiler is not None:
271
416
  profiler.mark("agent.construct.start")
@@ -283,6 +428,7 @@ def _resolve_session(
283
428
  cwd: Path | None = None,
284
429
  profiler: StartupProfiler | None = None,
285
430
  ) -> tuple[ChatSession, str]:
431
+ ChatSession = _import_chat_session()
286
432
  if resume_session_id:
287
433
  if profiler is not None:
288
434
  profiler.mark("session.resume.construct.start")
@@ -388,27 +534,17 @@ async def _run_print_mode(
388
534
  message: str,
389
535
  *,
390
536
  project_root: Path,
537
+ output_format: str = "text",
391
538
  ) -> None:
392
- session = ChatSession(agent, cwd=project_root, persistent=False)
393
- try:
394
- await _preload_mcp_in_tui(session)
395
-
396
- final_text = ""
397
- async for event in session.query_stream(message):
398
- if isinstance(event, TextEvent):
399
- final_text = event.content
400
-
401
- if final_text:
402
- sys.stdout.write(final_text)
403
- if not final_text.endswith("\n"):
404
- sys.stdout.write("\n")
405
- sys.stdout.flush()
406
- except Exception as exc:
407
- logger.error(f"Print mode failed: {exc}", exc_info=True)
408
- sys.stderr.write(f"Error: {exc}\n")
409
- raise SystemExit(1) from exc
410
- finally:
411
- await _graceful_shutdown(session)
539
+ run_print_mode = _import_print_mode_runner()
540
+ await run_print_mode(
541
+ agent,
542
+ message,
543
+ project_root=project_root,
544
+ output_format=output_format,
545
+ preload_mcp=_preload_mcp_in_tui,
546
+ graceful_shutdown=_graceful_shutdown,
547
+ )
412
548
 
413
549
 
414
550
  def _install_event_loop_exception_handler() -> None:
@@ -442,6 +578,7 @@ async def run(
442
578
  resume_session_id: str | None = None,
443
579
  resume_select: bool = False,
444
580
  print_message: str | None = None,
581
+ print_output_format: str = "text",
445
582
  process_start_perf: float | None = None,
446
583
  app_import_start_perf: float | None = None,
447
584
  app_import_done_perf: float | None = None,
@@ -452,20 +589,34 @@ async def run(
452
589
  profiler = StartupProfiler.from_env(logger=logger, started_at=process_start_perf)
453
590
  if app_import_start_perf is not None:
454
591
  profiler.mark_at("main.app_import.start", app_import_start_perf)
592
+ profiler.mark_at("main.import.app.start", app_import_start_perf)
455
593
  if app_import_done_perf is not None:
456
594
  profiler.mark_at("main.app_import.done", app_import_done_perf)
595
+ profiler.mark_at("main.import.app.done", app_import_done_perf)
457
596
  profiler.mark("cli_project_root.start")
458
597
  project_root = _resolve_cli_project_root()
459
598
  profiler.mark("cli_project_root.done")
460
599
  profiler.mark("preflight.start")
600
+ run_preflight_if_needed = _import_preflight_runner()
601
+ preflight_console = console
602
+ if print_message is not None and print_output_format == "json":
603
+ preflight_console = Console(stderr=True)
461
604
  preflight_result = await run_preflight_if_needed(
462
- console=console,
605
+ console=preflight_console,
463
606
  project_root=project_root,
464
607
  interactive=not rpc_stdio and print_message is None,
465
608
  )
466
609
  profiler.mark("preflight.done")
467
610
  if preflight_result.should_abort_launch:
468
611
  if print_message is not None:
612
+ if print_output_format == "json":
613
+ write_json_error_result = _import_print_mode_json_error_writer()
614
+ detail = preflight_result.detail or preflight_result.status
615
+ write_json_error_result(
616
+ error=f"Preflight failed: {detail}",
617
+ stop_reason=None,
618
+ session_id=None,
619
+ )
469
620
  raise SystemExit(1)
470
621
  return
471
622
 
@@ -473,6 +624,12 @@ async def run(
473
624
  print_logo(console, project_root=project_root)
474
625
  profiler.mark("logo.printed")
475
626
 
627
+ (
628
+ _check_update_func,
629
+ get_pending_update_prompt_info,
630
+ _record_update_check_attempt,
631
+ _should_check_for_update,
632
+ ) = _import_update_check_core()
476
633
  pending_info = get_pending_update_prompt_info()
477
634
  if pending_info is not None:
478
635
  should_stop = await _handle_update_on_launch(pending_info)
@@ -480,7 +637,13 @@ async def run(
480
637
  return
481
638
 
482
639
  profiler.mark("agent.build.start")
483
- agent = _build_agent(project_root=project_root, profiler=profiler)
640
+ agent = _build_agent(
641
+ project_root=project_root,
642
+ profiler=profiler,
643
+ extra_disallowed_tools=(
644
+ PRINT_MODE_DISALLOWED_TOOLS if print_message is not None else None
645
+ ),
646
+ )
484
647
  profiler.mark("agent.build.done")
485
648
 
486
649
  if rpc_stdio and resume_select and not resume_session_id:
@@ -494,6 +657,7 @@ async def run(
494
657
  cwd=project_root,
495
658
  profiler=profiler,
496
659
  )
660
+ StdioRPCBridge = _import_rpc_bridge()
497
661
  bridge = StdioRPCBridge(session)
498
662
  try:
499
663
  await bridge.run()
@@ -503,10 +667,16 @@ async def run(
503
667
 
504
668
  # --- Print mode branch ---
505
669
  if print_message is not None:
506
- await _run_print_mode(agent, print_message, project_root=project_root)
670
+ await _run_print_mode(
671
+ agent,
672
+ print_message,
673
+ project_root=project_root,
674
+ output_format=print_output_format,
675
+ )
507
676
  return
508
677
 
509
678
  if resume_select and not resume_session_id:
679
+ select_resume_session_id = _import_resume_selector()
510
680
  selected_session_id = await select_resume_session_id(console, cwd=project_root)
511
681
  if not selected_session_id:
512
682
  return
@@ -515,8 +685,8 @@ async def run(
515
685
  # 在 _resolve_session 前安装 TUILoggingHandler,确保 session 初始化期间
516
686
  # 的 logger.warning()(如 user_instruction token 超限)能被 TUI 系统捕获,
517
687
  # 而非 fallthrough 到 Python lastResort StreamHandler 以原始文本输出到 stderr。
688
+ EventRenderer, setup_tui_logging, StatusBar, TerminalAgentTUI = _import_interactive_tui_components(profiler)
518
689
  renderer = EventRenderer(project_root=project_root)
519
- from comate_cli.terminal_agent.logging_adapter import setup_tui_logging
520
690
  profiler.mark("logging.setup.start")
521
691
  logging_session = setup_tui_logging(renderer, project_root=project_root)
522
692
  profiler.mark("logging.setup.done")
@@ -550,6 +720,12 @@ async def run(
550
720
  profiler.mark("tui.init.done")
551
721
  tui.add_resume_history(mode)
552
722
  update_check_task: asyncio.Task[None] | None = None
723
+ (
724
+ _check_update_func,
725
+ _get_pending_update_prompt_info,
726
+ record_update_check_attempt,
727
+ should_check_for_update,
728
+ ) = _import_update_check_core()
553
729
  if should_check_for_update():
554
730
  # attempt 语义:先记录尝试时间,确保 interval 内最多发起一次网络检查
555
731
  record_update_check_attempt()
@@ -1128,15 +1128,17 @@ class EventRenderer:
1128
1128
  diff_lines: list[str] | None = None,
1129
1129
  model_name: str = "",
1130
1130
  subtitle: str | None = None,
1131
+ file_path: str | None = None,
1131
1132
  ) -> None:
1132
1133
  """Append a tool result to history as a static entry (no timer).
1133
1134
 
1134
1135
  Args:
1135
1136
  signature: 工具签名,例如 "Read(path=xxx)"
1136
1137
  is_error: 是否为错误结果
1137
- diff_lines: optional diff lines for Edit
1138
+ diff_lines: optional diff lines for Edit/Write
1138
1139
  model_name: model name for Agent tools (rendered dim)
1139
1140
  subtitle: optional subtitle rendered as `⎿ ...`
1141
+ file_path: optional source path used for syntax highlighting diff body
1140
1142
  """
1141
1143
  sev: Literal["info", "warning", "error"] = "error" if is_error else "info"
1142
1144
  sub_sty: Literal["error", "warning", "dim"] = "dim"
@@ -1145,7 +1147,7 @@ class EventRenderer:
1145
1147
  if model_name:
1146
1148
  text_obj.append(f" {BULLET_OPERATOR} {model_name}", style="dim")
1147
1149
  text_obj.append("\n")
1148
- text_obj.append(render_diff_text(diff_lines))
1150
+ text_obj.append(render_diff_text(diff_lines, file_path=file_path))
1149
1151
  self._append_history_entry(
1150
1152
  HistoryEntry(entry_type="tool_result", text=text_obj, severity="info", subtitle=subtitle)
1151
1153
  )