comate-cli 0.7.3__tar.gz → 0.7.5__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.3 → comate_cli-0.7.5}/PKG-INFO +2 -2
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/app.py +98 -27
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/status_bar.py +46 -13
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/tui.py +75 -12
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/update_check.py +23 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/pyproject.toml +2 -2
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_app_startup_latency.py +9 -3
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_completion_status_panel.py +1 -1
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_startup_profile.py +124 -4
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_status_bar.py +97 -12
- comate_cli-0.7.5/tests/test_tui_startup_latency.py +276 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_update_check.py +46 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/uv.lock +2 -2
- comate_cli-0.7.3/tests/test_tui_startup_latency.py +0 -104
- {comate_cli-0.7.3 → comate_cli-0.7.5}/.gitignore +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/CHANGELOG.md +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/README.md +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/__init__.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/__main__.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/main.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/mcp_cli.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/__init__.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/animations.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/assistant_render.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/codenames.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/config/__init__.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/config/model.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/config/picker.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/config/picker_state.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/config/store.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/custom_slash_commands.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/env_utils.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/error_display.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/event_renderer.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/figures.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/fragment_utils.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/goal_resume_view.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/history_printer.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/input_geometry.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/layout_coordinator.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/logging_adapter.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/logo.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/markdown_render.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/mention_completer.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/message_style.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/models.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/path_context_hint.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/plugins/__init__.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/plugins/components/__init__.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/plugins/components/detail_view.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/plugins/components/plugin_list.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/plugins/components/search_box.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/plugins/components/tab_bar.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/plugins/marketplace_install_view.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/plugins/plugin_picker.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/plugins/tabs/__init__.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/plugins/tabs/discover_tab.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/plugins/tabs/errors_tab.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/plugins/tabs/installed_tab.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/plugins/tabs/marketplaces_tab.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/preflight.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/question_view.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/resume_picker.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/resume_preview.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/resume_selector.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/rpc_protocol.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/rpc_stdio.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/selection_menu.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/slash_commands.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/startup.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/startup_profile.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/statusline/__init__.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/statusline/model.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/statusline/picker.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/statusline/picker_state.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/statusline/store.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/text_effects.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/tips.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/tool_fold.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/tool_result_formatters.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/tool_result_store.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/tool_result_viewer.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/tool_view.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/transcript_viewer.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/tui_parts/__init__.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/tui_parts/btw_view.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/tui_parts/commands.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/tui_parts/history_sync.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/tui_parts/input_behavior.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/tui_parts/key_bindings.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/tui_parts/mcp_connecting_view.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/tui_parts/render_panels.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/tui_parts/slash_command_registry.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/comate_cli/terminal_agent/tui_parts/ui_mode.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/config/__init__.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/config/test_model.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/config/test_picker_state.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/config/test_picker_ui.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/config/test_roundtrip.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/config/test_store_load.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/config/test_store_save.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/conftest.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/fixtures/fake_mcp_misbehaving_stdout.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/statusline/__init__.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/statusline/test_model.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/statusline/test_picker_state.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/statusline/test_store.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_animator_shuffle.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_app_mcp_preload.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_app_preflight_gate.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_app_print_mode.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_app_shutdown.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_app_token_cost_config.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_app_usage_line.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_btw_slash_command.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_cli_project_root.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_compact_command_semantics.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_completion_context_activation.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_context_command.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_custom_slash_commands.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_discover_tab.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_errors_tab.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_event_renderer.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_event_renderer_boundary.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_event_renderer_e2e.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_event_renderer_log_boundary.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_event_renderer_log_queue.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_event_renderer_streaming.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_event_renderer_tool_fold.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_format_error.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_goal_resume_tui.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_goal_resume_view.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_goal_slash_command.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_handle_error.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_history_printer.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_history_printer_log.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_history_printer_subtitle_position.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_history_printer_tool_fold.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_history_sync.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_history_sync_tool_fold.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_input_behavior.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_input_history.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_installed_tab.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_interrupt_exit_semantics.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_layout_coordinator.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_logging_adapter.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_logo.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_main_args.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_markdown_render.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_marketplaces_tab.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_mcp_cli.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_mcp_slash_command.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_mention_completer.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_path_context_hint.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_plugin_slash_commands.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_plugin_tui_components.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_preflight.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_preflight_copilot.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_question_key_bindings.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_question_view.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_resume_picker.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_resume_preview.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_resume_selector.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_rewind_command_semantics.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_rpc_protocol.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_rpc_stdio_bridge.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_selection_menu.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_session_query_token_summary.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_shutdown_noise_guard.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_shutdown_noise_integration.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_skills_slash_command.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_slash_argument_hint.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_slash_clear.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_slash_completer.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_slash_registry.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_status_bar_transient.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_task_panel_format.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_task_panel_key_bindings.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_task_panel_rendering.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_task_poll.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_tool_fold.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_tool_fold_panel.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_tool_result_formatters.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_tool_result_store.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_tool_result_viewer.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_tool_result_viewer_key_bindings.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_tool_view.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_transcript_viewer.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_transcript_viewer_tool_fold.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_tui_elapsed_status.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_tui_esc_queue.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_tui_mcp_init_gate.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_tui_paste_newline_guard.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_tui_paste_placeholder.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_tui_queue_preview.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_tui_queue_sdk_source.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_tui_split_invariance.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_tui_team_messages.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_tui_thinking_display.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/tests/test_tui_tool_result_registry_lifecycle.py +0 -0
- {comate_cli-0.7.3 → comate_cli-0.7.5}/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.
|
|
3
|
+
Version: 0.7.5
|
|
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
|
|
@@ -13,7 +13,7 @@ Classifier: Programming Language :: Python :: 3
|
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.11
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.12
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
-
Requires-Python:
|
|
16
|
+
Requires-Python: <3.14,>=3.11
|
|
17
17
|
Requires-Dist: charset-normalizer==3.4.7
|
|
18
18
|
Requires-Dist: comate-agent-sdk<0.9.0,>=0.8.0a1
|
|
19
19
|
Requires-Dist: concurrent-log-handler>=0.9.25
|
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import logging
|
|
5
5
|
import os
|
|
6
|
-
import random
|
|
6
|
+
import random # noqa: F401 - tests patch this module-level import for deterministic startup tips.
|
|
7
7
|
import signal
|
|
8
8
|
import sys
|
|
9
9
|
import threading
|
|
@@ -49,8 +49,14 @@ def _resolve_cli_project_root() -> Path:
|
|
|
49
49
|
return Path.cwd().expanduser().resolve()
|
|
50
50
|
|
|
51
51
|
|
|
52
|
-
async def _check_update() -> UpdateInfo | None:
|
|
53
|
-
|
|
52
|
+
async def _check_update(*, profiler: StartupProfiler | None = None) -> UpdateInfo | None:
|
|
53
|
+
if profiler is not None:
|
|
54
|
+
profiler.mark("impl.start")
|
|
55
|
+
try:
|
|
56
|
+
return await check_update(log=logger, profiler=profiler)
|
|
57
|
+
finally:
|
|
58
|
+
if profiler is not None:
|
|
59
|
+
profiler.mark("impl.done")
|
|
54
60
|
|
|
55
61
|
|
|
56
62
|
async def _handle_update_on_launch(info: UpdateInfo) -> bool:
|
|
@@ -109,7 +115,7 @@ def _schedule_update_check_on_launch(
|
|
|
109
115
|
async def _run() -> None:
|
|
110
116
|
try:
|
|
111
117
|
profiler.mark("update_check.start")
|
|
112
|
-
update_info = await _check_update()
|
|
118
|
+
update_info = await _check_update(profiler=profiler.child("update_check"))
|
|
113
119
|
profiler.mark("update_check.done")
|
|
114
120
|
if update_info is not None:
|
|
115
121
|
await _handle_background_update_on_launch(
|
|
@@ -228,35 +234,64 @@ async def add(a: int, b: int) -> int:
|
|
|
228
234
|
return a + b
|
|
229
235
|
|
|
230
236
|
|
|
231
|
-
def _build_agent(
|
|
237
|
+
def _build_agent(
|
|
238
|
+
*,
|
|
239
|
+
project_root: Path | None = None,
|
|
240
|
+
profiler: StartupProfiler | None = None,
|
|
241
|
+
) -> Agent:
|
|
232
242
|
from comate_agent_sdk.agent.compaction import CompactionConfig
|
|
233
243
|
from comate_cli.terminal_agent.config import store
|
|
234
244
|
|
|
235
245
|
resolved_project_root = project_root or _resolve_cli_project_root()
|
|
246
|
+
if profiler is not None:
|
|
247
|
+
profiler.mark("agent.config.load.start")
|
|
236
248
|
snapshot = store.load()
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
)
|
|
249
|
+
if profiler is not None:
|
|
250
|
+
profiler.mark("agent.config.load.done")
|
|
251
|
+
|
|
252
|
+
agent_config = AgentConfig(
|
|
253
|
+
role="software_engineering",
|
|
254
|
+
cwd=resolved_project_root,
|
|
255
|
+
env_options=EnvOptions(system_env=True, git_env=True),
|
|
256
|
+
use_streaming_task=True,
|
|
257
|
+
include_cost=bool(getattr(snapshot, "token_cost_enabled", False)),
|
|
258
|
+
memory_background_enabled=snapshot.memory_background_enabled,
|
|
259
|
+
memory_dreaming_enabled=snapshot.memory_dreaming_enabled,
|
|
260
|
+
compaction=CompactionConfig(
|
|
261
|
+
threshold_ratio=snapshot.compaction_threshold_ratio,
|
|
262
|
+
),
|
|
251
263
|
)
|
|
264
|
+
if profiler is not None:
|
|
265
|
+
profiler.mark("agent.construct.start")
|
|
266
|
+
try:
|
|
267
|
+
return Agent(config=agent_config)
|
|
268
|
+
finally:
|
|
269
|
+
if profiler is not None:
|
|
270
|
+
profiler.mark("agent.construct.done")
|
|
252
271
|
|
|
253
272
|
|
|
254
273
|
def _resolve_session(
|
|
255
|
-
agent: Agent,
|
|
274
|
+
agent: Agent,
|
|
275
|
+
resume_session_id: str | None,
|
|
276
|
+
*,
|
|
277
|
+
cwd: Path | None = None,
|
|
278
|
+
profiler: StartupProfiler | None = None,
|
|
256
279
|
) -> tuple[ChatSession, str]:
|
|
257
280
|
if resume_session_id:
|
|
258
|
-
|
|
259
|
-
|
|
281
|
+
if profiler is not None:
|
|
282
|
+
profiler.mark("session.resume.construct.start")
|
|
283
|
+
try:
|
|
284
|
+
return ChatSession.resume(agent, session_id=resume_session_id, cwd=cwd), "resume"
|
|
285
|
+
finally:
|
|
286
|
+
if profiler is not None:
|
|
287
|
+
profiler.mark("session.resume.construct.done")
|
|
288
|
+
if profiler is not None:
|
|
289
|
+
profiler.mark("session.new.construct.start")
|
|
290
|
+
try:
|
|
291
|
+
return ChatSession(agent, cwd=cwd), "new"
|
|
292
|
+
finally:
|
|
293
|
+
if profiler is not None:
|
|
294
|
+
profiler.mark("session.new.construct.done")
|
|
260
295
|
|
|
261
296
|
|
|
262
297
|
def _format_exit_usage_line(usage: object) -> str:
|
|
@@ -287,23 +322,47 @@ def _format_resume_hint(session_id: str | None) -> str | None:
|
|
|
287
322
|
)
|
|
288
323
|
|
|
289
324
|
|
|
290
|
-
async def _preload_mcp_in_tui(
|
|
325
|
+
async def _preload_mcp_in_tui(
|
|
326
|
+
session: ChatSession,
|
|
327
|
+
*,
|
|
328
|
+
profiler: StartupProfiler | None = None,
|
|
329
|
+
) -> None:
|
|
291
330
|
"""在 TUI 内异步加载 MCP,初始化阶段不输出 scrollback 文案。"""
|
|
331
|
+
if profiler is not None:
|
|
332
|
+
profiler.mark("preload.start")
|
|
292
333
|
runtime = session.runtime
|
|
293
334
|
if not bool(runtime.config.mcp_enabled):
|
|
335
|
+
if profiler is not None:
|
|
336
|
+
profiler.mark("preload.skip_disabled")
|
|
294
337
|
return
|
|
295
338
|
|
|
296
339
|
try:
|
|
340
|
+
if profiler is not None:
|
|
341
|
+
profiler.mark("start_preload.start")
|
|
297
342
|
preload_task = runtime.start_mcp_preload()
|
|
343
|
+
if profiler is not None:
|
|
344
|
+
profiler.mark("start_preload.done")
|
|
298
345
|
if preload_task is None:
|
|
346
|
+
if profiler is not None:
|
|
347
|
+
profiler.mark("preload.no_task")
|
|
299
348
|
return
|
|
349
|
+
if profiler is not None:
|
|
350
|
+
profiler.mark("await_preload.start")
|
|
300
351
|
await preload_task
|
|
352
|
+
if profiler is not None:
|
|
353
|
+
profiler.mark("await_preload.done")
|
|
301
354
|
except Exception as e:
|
|
302
355
|
logger.debug(f"MCP init failed: {e}", exc_info=True)
|
|
356
|
+
if profiler is not None:
|
|
357
|
+
profiler.mark("preload.failed")
|
|
303
358
|
return
|
|
304
359
|
|
|
360
|
+
if profiler is not None:
|
|
361
|
+
profiler.mark("inspect_manager.start")
|
|
305
362
|
mgr = runtime._mcp_manager
|
|
306
363
|
if mgr is None:
|
|
364
|
+
if profiler is not None:
|
|
365
|
+
profiler.mark("inspect_manager.no_manager")
|
|
307
366
|
return
|
|
308
367
|
|
|
309
368
|
for alias, reason in mgr.failed_servers:
|
|
@@ -314,6 +373,8 @@ async def _preload_mcp_in_tui(session: ChatSession) -> None:
|
|
|
314
373
|
count = len(loaded)
|
|
315
374
|
aliases = sorted({i.server_alias for i in loaded})
|
|
316
375
|
logger.info(f"MCP Server loaded: {', '.join(aliases)} ({count} tools)")
|
|
376
|
+
if profiler is not None:
|
|
377
|
+
profiler.mark("inspect_manager.done")
|
|
317
378
|
|
|
318
379
|
|
|
319
380
|
async def _run_print_mode(
|
|
@@ -404,7 +465,7 @@ async def run(
|
|
|
404
465
|
return
|
|
405
466
|
|
|
406
467
|
profiler.mark("agent.build.start")
|
|
407
|
-
agent = _build_agent(project_root=project_root)
|
|
468
|
+
agent = _build_agent(project_root=project_root, profiler=profiler)
|
|
408
469
|
profiler.mark("agent.build.done")
|
|
409
470
|
|
|
410
471
|
if rpc_stdio and resume_select and not resume_session_id:
|
|
@@ -412,7 +473,12 @@ async def run(
|
|
|
412
473
|
return
|
|
413
474
|
|
|
414
475
|
if rpc_stdio:
|
|
415
|
-
session, _mode = _resolve_session(
|
|
476
|
+
session, _mode = _resolve_session(
|
|
477
|
+
agent,
|
|
478
|
+
resume_session_id,
|
|
479
|
+
cwd=project_root,
|
|
480
|
+
profiler=profiler,
|
|
481
|
+
)
|
|
416
482
|
bridge = StdioRPCBridge(session)
|
|
417
483
|
try:
|
|
418
484
|
await bridge.run()
|
|
@@ -441,7 +507,12 @@ async def run(
|
|
|
441
507
|
profiler.mark("logging.setup.done")
|
|
442
508
|
|
|
443
509
|
profiler.mark("session.resolve.start")
|
|
444
|
-
session, mode = _resolve_session(
|
|
510
|
+
session, mode = _resolve_session(
|
|
511
|
+
agent,
|
|
512
|
+
resume_session_id,
|
|
513
|
+
cwd=project_root,
|
|
514
|
+
profiler=profiler,
|
|
515
|
+
)
|
|
445
516
|
profiler.mark("session.resolve.done")
|
|
446
517
|
# setup_tui_logging 在 _resolve_session 前安装,用于捕获 session 初始化期 warning/error。
|
|
447
518
|
# 这些日志没有交互 anchor,需在恢复历史或首次用户输入前落为 standalone log。
|
|
@@ -469,7 +540,7 @@ async def run(
|
|
|
469
540
|
)
|
|
470
541
|
|
|
471
542
|
async def _mcp_loader() -> None:
|
|
472
|
-
await _preload_mcp_in_tui(session)
|
|
543
|
+
await _preload_mcp_in_tui(session, profiler=profiler.child("mcp"))
|
|
473
544
|
mgr = session.runtime._mcp_manager
|
|
474
545
|
if mgr and mgr.failed_servers:
|
|
475
546
|
aliases = [alias for alias, _ in mgr.failed_servers]
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import asyncio
|
|
3
4
|
import logging
|
|
4
5
|
import subprocess
|
|
5
6
|
import time
|
|
@@ -40,7 +41,7 @@ class StatusBar:
|
|
|
40
41
|
self._session = session
|
|
41
42
|
self._model_name: str = self._resolve_model_name(session)
|
|
42
43
|
self._mode: str = "act"
|
|
43
|
-
self._git_branch: str =
|
|
44
|
+
self._git_branch: str = "N/A"
|
|
44
45
|
self._context_used_pct: float = 0.0
|
|
45
46
|
self._context_left_pct: float = 100.0
|
|
46
47
|
self._git_diff_stats: GitDiffStats | None = None
|
|
@@ -205,6 +206,25 @@ class StatusBar:
|
|
|
205
206
|
turn = getattr(agent, "_turn_number", None)
|
|
206
207
|
return f"#{turn}" if turn is not None else "-"
|
|
207
208
|
|
|
209
|
+
def _context_percent_refresh_started(self) -> bool:
|
|
210
|
+
for owner in (
|
|
211
|
+
self._session,
|
|
212
|
+
getattr(self._session, "_agent", None),
|
|
213
|
+
getattr(getattr(self._session, "_agent", None), "_context", None),
|
|
214
|
+
):
|
|
215
|
+
if owner is None:
|
|
216
|
+
continue
|
|
217
|
+
turn = getattr(owner, "_turn_number", None)
|
|
218
|
+
if turn is None:
|
|
219
|
+
turn = getattr(owner, "turn_number", None)
|
|
220
|
+
if turn is None:
|
|
221
|
+
continue
|
|
222
|
+
try:
|
|
223
|
+
return int(turn) > 0
|
|
224
|
+
except (TypeError, ValueError):
|
|
225
|
+
continue
|
|
226
|
+
return True
|
|
227
|
+
|
|
208
228
|
def _resolve_project_cwd(self) -> str:
|
|
209
229
|
cwd = getattr(self._session, "_cwd", None)
|
|
210
230
|
if cwd is None:
|
|
@@ -259,23 +279,36 @@ class StatusBar:
|
|
|
259
279
|
return " ".join(parts) if parts else "-"
|
|
260
280
|
|
|
261
281
|
async def refresh(self) -> None:
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
282
|
+
if self._context_percent_refresh_started():
|
|
283
|
+
try:
|
|
284
|
+
ctx_info = await self._session.get_context_info()
|
|
285
|
+
utilization = float(getattr(ctx_info, "utilization_percent", 0.0))
|
|
286
|
+
except Exception:
|
|
287
|
+
return
|
|
288
|
+
|
|
289
|
+
normalized = max(0.0, min(utilization, 100.0))
|
|
290
|
+
self._context_used_pct = normalized
|
|
291
|
+
self._context_left_pct = max(0.0, 100.0 - normalized)
|
|
267
292
|
|
|
268
293
|
try:
|
|
269
294
|
self._mode = str(self._session.get_mode()).strip().lower() or "act"
|
|
270
295
|
except Exception:
|
|
271
296
|
pass
|
|
272
297
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
298
|
+
branch_task = asyncio.to_thread(self._resolve_git_branch)
|
|
299
|
+
now = time.monotonic()
|
|
300
|
+
diff_is_stale = (
|
|
301
|
+
self._git_diff_stats is None
|
|
302
|
+
or now - self._git_diff_cache_time >= self._GIT_DIFF_CACHE_SECONDS
|
|
303
|
+
)
|
|
304
|
+
if diff_is_stale:
|
|
305
|
+
diff_task = asyncio.to_thread(self._resolve_git_diff_stats)
|
|
306
|
+
branch, diff_stats = await asyncio.gather(branch_task, diff_task)
|
|
307
|
+
self._git_diff_stats = diff_stats
|
|
308
|
+
self._git_diff_cache_time = time.monotonic()
|
|
309
|
+
else:
|
|
310
|
+
branch = await branch_task
|
|
311
|
+
self._git_branch = branch
|
|
279
312
|
|
|
280
313
|
# Refresh token usage cache if any token-related items are enabled.
|
|
281
314
|
cfg = self._config
|
|
@@ -325,7 +358,7 @@ class StatusBar:
|
|
|
325
358
|
return max(24, min(width - 6, 72))
|
|
326
359
|
|
|
327
360
|
def context_left_text(self) -> str:
|
|
328
|
-
return f"{self._context_left_pct:.0f}%
|
|
361
|
+
return f"Context {self._context_left_pct:.0f}% left"
|
|
329
362
|
|
|
330
363
|
def _status_text_for_width(self, width: int) -> str:
|
|
331
364
|
mode_text = f"[{self._mode}]"
|
|
@@ -139,6 +139,16 @@ def _truncate_file_history(path: Path, max_entries: int = 200) -> None:
|
|
|
139
139
|
path.write_bytes(rewritten_bytes)
|
|
140
140
|
|
|
141
141
|
|
|
142
|
+
def _load_plugins_blocking(project_path: Path) -> tuple[list[Any], list[Any]]:
|
|
143
|
+
from comate_agent_sdk.plugins import PluginLoader, create_plugin_registry
|
|
144
|
+
|
|
145
|
+
loader = PluginLoader()
|
|
146
|
+
registry = create_plugin_registry()
|
|
147
|
+
data_dir = registry.base_dir / "data"
|
|
148
|
+
data_dir.mkdir(parents=True, exist_ok=True)
|
|
149
|
+
return loader.load_all(registry=registry, project_path=project_path)
|
|
150
|
+
|
|
151
|
+
|
|
142
152
|
class TerminalAgentTUI(
|
|
143
153
|
KeyBindingsMixin,
|
|
144
154
|
InputBehaviorMixin,
|
|
@@ -266,6 +276,7 @@ class TerminalAgentTUI(
|
|
|
266
276
|
self._ui_tick_task: asyncio.Task[None] | None = None
|
|
267
277
|
self._mcp_init_task: asyncio.Task[None] | None = None
|
|
268
278
|
self._plugin_init_task: asyncio.Task[None] | None = None
|
|
279
|
+
self._startup_status_refresh_task: asyncio.Task[None] | None = None
|
|
269
280
|
self._plugin_init_cancel_timeout_s = 1.0
|
|
270
281
|
self._interrupt_requested_at: float | None = None
|
|
271
282
|
self._interrupt_force_window_seconds = 1.5
|
|
@@ -1759,7 +1770,7 @@ class TerminalAgentTUI(
|
|
|
1759
1770
|
try:
|
|
1760
1771
|
if profiler is not None:
|
|
1761
1772
|
profiler.mark("plugins.init.start")
|
|
1762
|
-
await self._init_plugins()
|
|
1773
|
+
await self._init_plugins(profiler=profiler.child("plugins") if profiler is not None else None)
|
|
1763
1774
|
if profiler is not None:
|
|
1764
1775
|
profiler.mark("plugins.init.done")
|
|
1765
1776
|
except asyncio.CancelledError:
|
|
@@ -1773,6 +1784,8 @@ class TerminalAgentTUI(
|
|
|
1773
1784
|
finally:
|
|
1774
1785
|
self._refresh_layers()
|
|
1775
1786
|
|
|
1787
|
+
if profiler is not None:
|
|
1788
|
+
profiler.mark("plugins.task.create")
|
|
1776
1789
|
self._plugin_init_task = asyncio.create_task(
|
|
1777
1790
|
_do_plugin_init(),
|
|
1778
1791
|
name="terminal-plugin-init",
|
|
@@ -1792,16 +1805,42 @@ class TerminalAgentTUI(
|
|
|
1792
1805
|
finally:
|
|
1793
1806
|
self._refresh_layers()
|
|
1794
1807
|
|
|
1808
|
+
if profiler is not None:
|
|
1809
|
+
profiler.mark("mcp.task.create")
|
|
1795
1810
|
self._mcp_init_task = asyncio.create_task(
|
|
1796
1811
|
_do_init(),
|
|
1797
1812
|
name="terminal-mcp-init",
|
|
1798
1813
|
)
|
|
1799
1814
|
|
|
1815
|
+
if profiler is not None:
|
|
1816
|
+
profiler.mark("ui_tick.task.create")
|
|
1800
1817
|
self._ui_tick_task = asyncio.create_task(
|
|
1801
1818
|
self._ui_tick(),
|
|
1802
1819
|
name="terminal-ui-tick",
|
|
1803
1820
|
)
|
|
1821
|
+
status_bar = getattr(self, "_status_bar", None)
|
|
1822
|
+
if status_bar is not None:
|
|
1823
|
+
async def _do_startup_status_refresh() -> None:
|
|
1824
|
+
try:
|
|
1825
|
+
await status_bar.refresh()
|
|
1826
|
+
except asyncio.CancelledError:
|
|
1827
|
+
raise
|
|
1828
|
+
except Exception:
|
|
1829
|
+
logger.debug("startup status refresh failed", exc_info=True)
|
|
1830
|
+
finally:
|
|
1831
|
+
if not self._closing:
|
|
1832
|
+
self._refresh_layers()
|
|
1833
|
+
self._invalidate()
|
|
1834
|
+
|
|
1835
|
+
if profiler is not None:
|
|
1836
|
+
profiler.mark("status_bar.refresh.task.create")
|
|
1837
|
+
self._startup_status_refresh_task = asyncio.create_task(
|
|
1838
|
+
_do_startup_status_refresh(),
|
|
1839
|
+
name="terminal-startup-status-refresh",
|
|
1840
|
+
)
|
|
1804
1841
|
if hasattr(self, "_session") and self._session is not None:
|
|
1842
|
+
if profiler is not None:
|
|
1843
|
+
profiler.mark("event_pump.task.create")
|
|
1805
1844
|
self._event_pump_task = asyncio.create_task(
|
|
1806
1845
|
self._consume_event_stream(),
|
|
1807
1846
|
name="terminal-session-event-pump",
|
|
@@ -1841,6 +1880,16 @@ class TerminalAgentTUI(
|
|
|
1841
1880
|
self._ui_tick_task.cancel()
|
|
1842
1881
|
with suppress(asyncio.CancelledError):
|
|
1843
1882
|
await self._ui_tick_task
|
|
1883
|
+
startup_status_refresh_task = getattr(
|
|
1884
|
+
self,
|
|
1885
|
+
"_startup_status_refresh_task",
|
|
1886
|
+
None,
|
|
1887
|
+
)
|
|
1888
|
+
if startup_status_refresh_task is not None:
|
|
1889
|
+
startup_status_refresh_task.cancel()
|
|
1890
|
+
with suppress(asyncio.CancelledError):
|
|
1891
|
+
await startup_status_refresh_task
|
|
1892
|
+
self._startup_status_refresh_task = None
|
|
1844
1893
|
event_pump_task = getattr(self, "_event_pump_task", None)
|
|
1845
1894
|
if event_pump_task is not None:
|
|
1846
1895
|
event_pump_task.cancel()
|
|
@@ -1848,21 +1897,35 @@ class TerminalAgentTUI(
|
|
|
1848
1897
|
await event_pump_task
|
|
1849
1898
|
self._renderer.close()
|
|
1850
1899
|
|
|
1851
|
-
|
|
1900
|
+
def _plugin_project_path(self) -> Path:
|
|
1901
|
+
session = getattr(self, "_session", None)
|
|
1902
|
+
session_cwd = getattr(session, "_cwd", None)
|
|
1903
|
+
if session_cwd:
|
|
1904
|
+
return Path(session_cwd).expanduser().resolve()
|
|
1905
|
+
return Path.cwd().expanduser().resolve()
|
|
1906
|
+
|
|
1907
|
+
async def _init_plugins(self, *, profiler: Any | None = None) -> None:
|
|
1852
1908
|
"""Load and inject plugin resources at startup."""
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
data_dir = registry.base_dir / "data"
|
|
1859
|
-
data_dir.mkdir(parents=True, exist_ok=True)
|
|
1860
|
-
self._loaded_plugins, self._plugin_errors = loader.load_all(
|
|
1861
|
-
registry=registry,
|
|
1862
|
-
project_path=Path.cwd(),
|
|
1909
|
+
if profiler is not None:
|
|
1910
|
+
profiler.mark("load_thread.start")
|
|
1911
|
+
loaded_plugins, plugin_errors = await asyncio.to_thread(
|
|
1912
|
+
_load_plugins_blocking,
|
|
1913
|
+
self._plugin_project_path(),
|
|
1863
1914
|
)
|
|
1915
|
+
if profiler is not None:
|
|
1916
|
+
profiler.mark("load_thread.done")
|
|
1917
|
+
if self._closing:
|
|
1918
|
+
logger.debug("Plugin load completed after TUI close; discarding results")
|
|
1919
|
+
return
|
|
1920
|
+
|
|
1921
|
+
self._loaded_plugins = loaded_plugins
|
|
1922
|
+
self._plugin_errors = plugin_errors
|
|
1864
1923
|
self._plugin_command_names: set[str] = set()
|
|
1924
|
+
if profiler is not None:
|
|
1925
|
+
profiler.mark("inject.start")
|
|
1865
1926
|
self._inject_plugin_resources(self._loaded_plugins)
|
|
1927
|
+
if profiler is not None:
|
|
1928
|
+
profiler.mark("inject.done")
|
|
1866
1929
|
logger.info(
|
|
1867
1930
|
"Loaded %d plugins (%d errors)",
|
|
1868
1931
|
len(self._loaded_plugins),
|
|
@@ -70,33 +70,53 @@ async def check_update(
|
|
|
70
70
|
package: str = PACKAGE_NAME,
|
|
71
71
|
release_notes_url: str = RELEASE_NOTES_URL,
|
|
72
72
|
log: logging.Logger | None = None,
|
|
73
|
+
profiler: Any | None = None,
|
|
73
74
|
) -> UpdateInfo | None:
|
|
74
75
|
"""异步检查 comate-cli 是否有新版本,返回结构化版本信息或 None。"""
|
|
75
76
|
active_logger = log or logger
|
|
76
77
|
try:
|
|
78
|
+
if profiler is not None:
|
|
79
|
+
profiler.mark("current_version.start")
|
|
77
80
|
current = importlib.metadata.version(package)
|
|
78
81
|
except importlib.metadata.PackageNotFoundError:
|
|
79
82
|
return None
|
|
83
|
+
finally:
|
|
84
|
+
if profiler is not None:
|
|
85
|
+
profiler.mark("current_version.done")
|
|
80
86
|
|
|
87
|
+
if profiler is not None:
|
|
88
|
+
profiler.mark("locale.start")
|
|
81
89
|
if _is_chinese_locale():
|
|
82
90
|
url = f"https://mirrors.tuna.tsinghua.edu.cn/pypi/{package}/json"
|
|
83
91
|
source_label = "tuna"
|
|
84
92
|
else:
|
|
85
93
|
url = f"https://pypi.org/pypi/{package}/json"
|
|
86
94
|
source_label = "pypi"
|
|
95
|
+
if profiler is not None:
|
|
96
|
+
profiler.mark("locale.done")
|
|
87
97
|
|
|
88
98
|
try:
|
|
99
|
+
if profiler is not None:
|
|
100
|
+
profiler.mark("httpx_import.start")
|
|
89
101
|
import httpx
|
|
102
|
+
if profiler is not None:
|
|
103
|
+
profiler.mark("httpx_import.done")
|
|
90
104
|
|
|
91
105
|
async with httpx.AsyncClient(timeout=3.0) as client:
|
|
106
|
+
if profiler is not None:
|
|
107
|
+
profiler.mark("http_get.start")
|
|
92
108
|
resp = await client.get(url)
|
|
93
109
|
resp.raise_for_status()
|
|
94
110
|
latest = resp.json()["info"]["version"]
|
|
111
|
+
if profiler is not None:
|
|
112
|
+
profiler.mark("http_get.done")
|
|
95
113
|
except Exception:
|
|
96
114
|
active_logger.debug(f"update check failed (source={source_label})", exc_info=True)
|
|
97
115
|
return None
|
|
98
116
|
|
|
99
117
|
try:
|
|
118
|
+
if profiler is not None:
|
|
119
|
+
profiler.mark("version_compare.start")
|
|
100
120
|
from packaging.version import Version
|
|
101
121
|
|
|
102
122
|
if Version(latest) > Version(current):
|
|
@@ -113,6 +133,9 @@ async def check_update(
|
|
|
113
133
|
latest,
|
|
114
134
|
exc_info=True,
|
|
115
135
|
)
|
|
136
|
+
finally:
|
|
137
|
+
if profiler is not None:
|
|
138
|
+
profiler.mark("version_compare.done")
|
|
116
139
|
return None
|
|
117
140
|
|
|
118
141
|
|
|
@@ -4,10 +4,10 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "comate-cli"
|
|
7
|
-
version = "0.7.
|
|
7
|
+
version = "0.7.5"
|
|
8
8
|
description = "Comate terminal CLI built on comate-agent-sdk"
|
|
9
9
|
readme = "README.md"
|
|
10
|
-
requires-python = ">=3.11"
|
|
10
|
+
requires-python = ">=3.11,<3.14"
|
|
11
11
|
authors = [
|
|
12
12
|
{ name = "Andy", email = "andy.dev@aliyun.com" }
|
|
13
13
|
]
|
|
@@ -64,7 +64,11 @@ class TestAppStartupLatency(unittest.IsolatedAsyncioTestCase):
|
|
|
64
64
|
"run_preflight_if_needed",
|
|
65
65
|
AsyncMock(return_value=PreflightResult(status="configured")),
|
|
66
66
|
),
|
|
67
|
-
patch.object(
|
|
67
|
+
patch.object(
|
|
68
|
+
app_module,
|
|
69
|
+
"_build_agent",
|
|
70
|
+
side_effect=lambda *, project_root, profiler=None: order.append("build_agent") or object(),
|
|
71
|
+
),
|
|
68
72
|
patch.object(app_module, "print_logo", side_effect=lambda *args, **kwargs: order.append("print_logo")),
|
|
69
73
|
patch.object(app_module, "_check_update", AsyncMock(return_value=None)),
|
|
70
74
|
patch.object(app_module, "EventRenderer", return_value=MagicMock()),
|
|
@@ -149,7 +153,8 @@ class TestAppStartupLatency(unittest.IsolatedAsyncioTestCase):
|
|
|
149
153
|
await asyncio.wait_for(update_started.wait(), timeout=0.1)
|
|
150
154
|
self.run_called.set()
|
|
151
155
|
|
|
152
|
-
async def _never_finishes() -> UpdateInfo | None:
|
|
156
|
+
async def _never_finishes(*, profiler=None) -> UpdateInfo | None:
|
|
157
|
+
del profiler
|
|
153
158
|
update_started.set()
|
|
154
159
|
try:
|
|
155
160
|
await asyncio.Event().wait()
|
|
@@ -190,7 +195,8 @@ class TestAppStartupLatency(unittest.IsolatedAsyncioTestCase):
|
|
|
190
195
|
super().__init__(session, status_bar, renderer)
|
|
191
196
|
created_tuis.append(self)
|
|
192
197
|
|
|
193
|
-
async def _raises() -> UpdateInfo | None:
|
|
198
|
+
async def _raises(*, profiler=None) -> UpdateInfo | None:
|
|
199
|
+
del profiler
|
|
194
200
|
raise RuntimeError("network down")
|
|
195
201
|
|
|
196
202
|
with (
|
|
@@ -34,7 +34,7 @@ class _FakeStatusBar:
|
|
|
34
34
|
return "act"
|
|
35
35
|
|
|
36
36
|
def info_status_text(self) -> str:
|
|
37
|
-
return "model | ~main / 100%
|
|
37
|
+
return "model | ~main / Context 100% left"
|
|
38
38
|
|
|
39
39
|
def info_status_fragments(self) -> list[tuple[str, str]]:
|
|
40
40
|
return [("class:status", self.info_status_text())]
|