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.
- {comate_cli-0.7.7 → comate_cli-0.7.9}/PKG-INFO +1 -1
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/main.py +113 -11
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/app.py +233 -57
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/event_renderer.py +4 -2
- comate_cli-0.7.9/comate_cli/terminal_agent/preflight.py +210 -0
- 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
- comate_cli-0.7.9/comate_cli/terminal_agent/print_mode.py +224 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/status_bar.py +3 -5
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/statusline/model.py +1 -5
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/statusline/picker_state.py +0 -1
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/tool_result_formatters.py +156 -28
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/tui_parts/history_sync.py +48 -10
- {comate_cli-0.7.7 → comate_cli-0.7.9}/pyproject.toml +1 -1
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/statusline/test_model.py +3 -5
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/statusline/test_picker_state.py +3 -4
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/statusline/test_store.py +17 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_app_preflight_gate.py +11 -9
- comate_cli-0.7.9/tests/test_app_print_mode.py +375 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_app_shutdown.py +20 -7
- comate_cli-0.7.9/tests/test_app_startup_latency.py +578 -0
- comate_cli-0.7.9/tests/test_app_token_cost_config.py +164 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_event_renderer.py +63 -3
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_history_sync.py +185 -3
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_main_args.py +122 -11
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_preflight.py +51 -6
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_preflight_copilot.py +1 -1
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_resume_picker.py +5 -0
- comate_cli-0.7.9/tests/test_startup_import_budget.py +118 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_startup_profile.py +75 -11
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_status_bar.py +17 -2
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_tool_result_formatters.py +178 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_update_check.py +106 -48
- {comate_cli-0.7.7 → comate_cli-0.7.9}/uv.lock +2 -2
- comate_cli-0.7.7/tests/test_app_print_mode.py +0 -90
- comate_cli-0.7.7/tests/test_app_startup_latency.py +0 -304
- comate_cli-0.7.7/tests/test_app_token_cost_config.py +0 -77
- {comate_cli-0.7.7 → comate_cli-0.7.9}/.gitignore +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/CHANGELOG.md +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/README.md +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/__init__.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/__main__.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/mcp_cli.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/__init__.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/animations.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/assistant_render.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/codenames.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/config/__init__.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/config/model.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/config/picker.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/config/picker_state.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/config/store.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/custom_slash_commands.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/env_utils.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/error_display.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/figures.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/fragment_utils.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/goal_resume_view.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/history_printer.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/input_geometry.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/layout_coordinator.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/logging_adapter.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/logo.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/markdown_render.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/mention_completer.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/message_style.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/models.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/path_context_hint.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/plugins/__init__.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/plugins/components/__init__.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/plugins/components/detail_view.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/plugins/components/plugin_list.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/plugins/components/search_box.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/plugins/components/tab_bar.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/plugins/marketplace_install_view.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/plugins/plugin_picker.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/plugins/tabs/__init__.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/plugins/tabs/discover_tab.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/plugins/tabs/errors_tab.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/plugins/tabs/installed_tab.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/plugins/tabs/marketplaces_tab.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/question_view.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/resume_picker.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/resume_preview.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/resume_selector.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/rpc_protocol.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/rpc_stdio.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/selection_menu.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/slash_commands.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/startup.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/startup_profile.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/statusline/__init__.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/statusline/picker.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/statusline/store.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/text_effects.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/tips.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/tool_fold.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/tool_result_store.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/tool_result_viewer.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/tool_view.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/transcript_viewer.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/tui.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/tui_parts/__init__.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/tui_parts/btw_view.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/tui_parts/commands.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/tui_parts/input_behavior.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/tui_parts/key_bindings.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/tui_parts/mcp_connecting_view.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/tui_parts/render_panels.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/tui_parts/slash_command_registry.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/tui_parts/ui_mode.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/comate_cli/terminal_agent/update_check.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/config/__init__.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/config/test_model.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/config/test_picker_state.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/config/test_picker_ui.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/config/test_roundtrip.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/config/test_store_load.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/config/test_store_save.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/conftest.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/fixtures/fake_mcp_misbehaving_stdout.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/statusline/__init__.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_animator_shuffle.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_app_mcp_preload.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_app_usage_line.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_btw_slash_command.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_cli_project_root.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_compact_command_semantics.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_completion_context_activation.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_completion_status_panel.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_context_command.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_custom_slash_commands.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_discover_tab.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_errors_tab.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_event_renderer_boundary.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_event_renderer_e2e.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_event_renderer_log_boundary.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_event_renderer_log_queue.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_event_renderer_streaming.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_event_renderer_tool_fold.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_format_error.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_goal_resume_tui.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_goal_resume_view.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_goal_slash_command.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_handle_error.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_history_printer.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_history_printer_log.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_history_printer_subtitle_position.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_history_printer_tool_fold.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_history_sync_tool_fold.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_input_behavior.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_input_history.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_installed_tab.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_interrupt_exit_semantics.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_layout_coordinator.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_logging_adapter.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_logo.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_markdown_render.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_marketplaces_tab.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_mcp_cli.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_mcp_slash_command.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_mention_completer.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_path_context_hint.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_plugin_slash_commands.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_plugin_tui_components.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_question_key_bindings.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_question_view.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_resume_preview.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_resume_selector.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_rewind_command_semantics.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_rpc_protocol.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_rpc_stdio_bridge.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_selection_menu.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_session_query_token_summary.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_shutdown_noise_guard.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_shutdown_noise_integration.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_skills_slash_command.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_slash_argument_hint.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_slash_clear.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_slash_completer.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_slash_registry.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_status_bar_transient.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_task_panel_format.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_task_panel_key_bindings.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_task_panel_rendering.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_task_poll.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_tool_fold.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_tool_fold_panel.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_tool_result_store.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_tool_result_viewer.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_tool_result_viewer_key_bindings.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_tool_view.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_transcript_viewer.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_transcript_viewer_tool_fold.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_tui_elapsed_status.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_tui_esc_queue.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_tui_mcp_init_gate.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_tui_paste_newline_guard.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_tui_paste_placeholder.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_tui_queue_preview.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_tui_queue_sdk_source.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_tui_split_invariance.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_tui_startup_latency.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_tui_team_messages.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_tui_thinking_display.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_tui_tool_result_registry_lifecycle.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.9}/tests/test_usage_command.py +0 -0
|
@@ -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]) ->
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
from
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
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
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
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=
|
|
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(
|
|
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(
|
|
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
|
)
|