comate-cli 0.7.7__tar.gz → 0.7.8__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.8}/PKG-INFO +1 -1
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/app.py +165 -33
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/event_renderer.py +4 -2
- comate_cli-0.7.8/comate_cli/terminal_agent/preflight.py +210 -0
- comate_cli-0.7.7/comate_cli/terminal_agent/preflight.py → comate_cli-0.7.8/comate_cli/terminal_agent/preflight_wizard.py +14 -202
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/status_bar.py +3 -5
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/statusline/model.py +1 -5
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/statusline/picker_state.py +0 -1
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/tool_result_formatters.py +156 -28
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/tui_parts/history_sync.py +48 -10
- {comate_cli-0.7.7 → comate_cli-0.7.8}/pyproject.toml +1 -1
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/statusline/test_model.py +3 -5
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/statusline/test_picker_state.py +3 -4
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/statusline/test_store.py +17 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_app_preflight_gate.py +11 -9
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_app_print_mode.py +12 -3
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_app_shutdown.py +20 -7
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_app_startup_latency.py +225 -31
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_app_token_cost_config.py +22 -3
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_event_renderer.py +63 -3
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_history_sync.py +185 -3
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_preflight.py +51 -6
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_preflight_copilot.py +1 -1
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_resume_picker.py +5 -0
- comate_cli-0.7.8/tests/test_startup_import_budget.py +118 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_startup_profile.py +75 -11
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_status_bar.py +17 -2
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_tool_result_formatters.py +178 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_update_check.py +106 -48
- {comate_cli-0.7.7 → comate_cli-0.7.8}/uv.lock +2 -2
- {comate_cli-0.7.7 → comate_cli-0.7.8}/.gitignore +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/CHANGELOG.md +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/README.md +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/__init__.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/__main__.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/main.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/mcp_cli.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/__init__.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/animations.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/assistant_render.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/codenames.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/config/__init__.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/config/model.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/config/picker.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/config/picker_state.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/config/store.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/custom_slash_commands.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/env_utils.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/error_display.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/figures.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/fragment_utils.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/goal_resume_view.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/history_printer.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/input_geometry.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/layout_coordinator.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/logging_adapter.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/logo.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/markdown_render.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/mention_completer.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/message_style.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/models.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/path_context_hint.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/plugins/__init__.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/plugins/components/__init__.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/plugins/components/detail_view.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/plugins/components/plugin_list.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/plugins/components/search_box.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/plugins/components/tab_bar.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/plugins/marketplace_install_view.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/plugins/plugin_picker.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/plugins/tabs/__init__.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/plugins/tabs/discover_tab.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/plugins/tabs/errors_tab.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/plugins/tabs/installed_tab.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/plugins/tabs/marketplaces_tab.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/question_view.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/resume_picker.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/resume_preview.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/resume_selector.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/rpc_protocol.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/rpc_stdio.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/selection_menu.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/slash_commands.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/startup.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/startup_profile.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/statusline/__init__.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/statusline/picker.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/statusline/store.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/text_effects.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/tips.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/tool_fold.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/tool_result_store.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/tool_result_viewer.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/tool_view.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/transcript_viewer.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/tui.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/tui_parts/__init__.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/tui_parts/btw_view.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/tui_parts/commands.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/tui_parts/input_behavior.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/tui_parts/key_bindings.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/tui_parts/mcp_connecting_view.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/tui_parts/render_panels.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/tui_parts/slash_command_registry.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/tui_parts/ui_mode.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/comate_cli/terminal_agent/update_check.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/config/__init__.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/config/test_model.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/config/test_picker_state.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/config/test_picker_ui.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/config/test_roundtrip.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/config/test_store_load.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/config/test_store_save.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/conftest.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/fixtures/fake_mcp_misbehaving_stdout.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/statusline/__init__.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_animator_shuffle.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_app_mcp_preload.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_app_usage_line.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_btw_slash_command.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_cli_project_root.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_compact_command_semantics.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_completion_context_activation.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_completion_status_panel.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_context_command.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_custom_slash_commands.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_discover_tab.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_errors_tab.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_event_renderer_boundary.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_event_renderer_e2e.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_event_renderer_log_boundary.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_event_renderer_log_queue.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_event_renderer_streaming.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_event_renderer_tool_fold.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_format_error.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_goal_resume_tui.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_goal_resume_view.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_goal_slash_command.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_handle_error.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_history_printer.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_history_printer_log.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_history_printer_subtitle_position.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_history_printer_tool_fold.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_history_sync_tool_fold.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_input_behavior.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_input_history.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_installed_tab.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_interrupt_exit_semantics.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_layout_coordinator.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_logging_adapter.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_logo.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_main_args.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_markdown_render.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_marketplaces_tab.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_mcp_cli.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_mcp_slash_command.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_mention_completer.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_path_context_hint.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_plugin_slash_commands.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_plugin_tui_components.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_question_key_bindings.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_question_view.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_resume_preview.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_resume_selector.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_rewind_command_semantics.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_rpc_protocol.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_rpc_stdio_bridge.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_selection_menu.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_session_query_token_summary.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_shutdown_noise_guard.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_shutdown_noise_integration.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_skills_slash_command.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_slash_argument_hint.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_slash_clear.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_slash_completer.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_slash_registry.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_status_bar_transient.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_task_panel_format.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_task_panel_key_bindings.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_task_panel_rendering.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_task_poll.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_tool_fold.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_tool_fold_panel.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_tool_result_store.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_tool_result_viewer.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_tool_result_viewer_key_bindings.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_tool_view.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_transcript_viewer.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_transcript_viewer_tool_fold.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_tui_elapsed_status.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_tui_esc_queue.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_tui_mcp_init_gate.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_tui_paste_newline_guard.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_tui_paste_placeholder.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_tui_queue_preview.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_tui_queue_sdk_source.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_tui_split_invariance.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_tui_startup_latency.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_tui_team_messages.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_tui_thinking_display.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_tui_tool_result_registry_lifecycle.py +0 -0
- {comate_cli-0.7.7 → comate_cli-0.7.8}/tests/test_usage_command.py +0 -0
|
@@ -11,36 +11,18 @@ import time
|
|
|
11
11
|
from collections.abc import Iterator
|
|
12
12
|
from contextlib import contextmanager, suppress
|
|
13
13
|
from pathlib import Path
|
|
14
|
+
from typing import TYPE_CHECKING
|
|
14
15
|
|
|
15
16
|
from rich.console import Console
|
|
16
17
|
|
|
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
18
|
from comate_cli.terminal_agent.startup_profile import StartupProfiler
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
from
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
-
)
|
|
19
|
+
|
|
20
|
+
if TYPE_CHECKING:
|
|
21
|
+
from comate_agent_sdk.agent.chat_session import ChatSession
|
|
22
|
+
from comate_agent_sdk.agent.core.template import AgentTemplate as Agent
|
|
23
|
+
from comate_agent_sdk.agent.events import TextEvent
|
|
24
|
+
from comate_cli.terminal_agent.event_renderer import EventRenderer
|
|
25
|
+
from comate_cli.terminal_agent.update_check import UpdateInfo
|
|
44
26
|
|
|
45
27
|
console = Console()
|
|
46
28
|
logger = logging.getLogger(__name__)
|
|
@@ -56,6 +38,7 @@ async def _check_update(*, profiler: StartupProfiler | None = None) -> UpdateInf
|
|
|
56
38
|
if profiler is not None:
|
|
57
39
|
profiler.mark("impl.start")
|
|
58
40
|
try:
|
|
41
|
+
check_update, _, _, _ = _import_update_check_core()
|
|
59
42
|
return await check_update(log=logger, profiler=profiler)
|
|
60
43
|
finally:
|
|
61
44
|
if profiler is not None:
|
|
@@ -67,6 +50,16 @@ async def _handle_update_on_launch(info: UpdateInfo) -> bool:
|
|
|
67
50
|
|
|
68
51
|
Returns True when launch should stop after a successful update.
|
|
69
52
|
"""
|
|
53
|
+
(
|
|
54
|
+
UpdatePromptChoice,
|
|
55
|
+
UpdatePromptDecision,
|
|
56
|
+
decide_update_prompt,
|
|
57
|
+
format_update_hint,
|
|
58
|
+
mark_update_seen,
|
|
59
|
+
record_skip_until_next_version,
|
|
60
|
+
run_update_command,
|
|
61
|
+
show_update_prompt,
|
|
62
|
+
) = _import_update_prompt_components()
|
|
70
63
|
try:
|
|
71
64
|
decision = decide_update_prompt(info)
|
|
72
65
|
if decision == UpdatePromptDecision.SKIP:
|
|
@@ -92,6 +85,16 @@ async def _handle_background_update_on_launch(
|
|
|
92
85
|
renderer: EventRenderer,
|
|
93
86
|
profiler: StartupProfiler,
|
|
94
87
|
) -> None:
|
|
88
|
+
(
|
|
89
|
+
_UpdatePromptChoice,
|
|
90
|
+
UpdatePromptDecision,
|
|
91
|
+
decide_update_prompt,
|
|
92
|
+
format_update_hint,
|
|
93
|
+
mark_update_seen,
|
|
94
|
+
_record_skip_until_next_version,
|
|
95
|
+
_run_update_command,
|
|
96
|
+
_show_update_prompt,
|
|
97
|
+
) = _import_update_prompt_components()
|
|
95
98
|
try:
|
|
96
99
|
decision = decide_update_prompt(info)
|
|
97
100
|
if decision == UpdatePromptDecision.SKIP:
|
|
@@ -198,6 +201,121 @@ def _sigint_guard() -> Iterator[None]:
|
|
|
198
201
|
logger.warning(f"Failed to restore SIGINT handler: {exc}", exc_info=True)
|
|
199
202
|
|
|
200
203
|
|
|
204
|
+
@contextmanager
|
|
205
|
+
def _profile_section(
|
|
206
|
+
profiler: StartupProfiler | None,
|
|
207
|
+
phase: str,
|
|
208
|
+
) -> Iterator[None]:
|
|
209
|
+
if profiler is not None:
|
|
210
|
+
profiler.mark(f"{phase}.start")
|
|
211
|
+
try:
|
|
212
|
+
yield
|
|
213
|
+
finally:
|
|
214
|
+
if profiler is not None:
|
|
215
|
+
profiler.mark(f"{phase}.done")
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def _import_preflight_runner():
|
|
219
|
+
from comate_cli.terminal_agent.preflight import run_preflight_if_needed
|
|
220
|
+
|
|
221
|
+
return run_preflight_if_needed
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def print_logo(*args, **kwargs) -> None:
|
|
225
|
+
from comate_cli.terminal_agent.logo import print_logo as _print_logo
|
|
226
|
+
|
|
227
|
+
_print_logo(*args, **kwargs)
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def _import_sdk_agent_components(
|
|
231
|
+
profiler: StartupProfiler | None = None,
|
|
232
|
+
) -> tuple[type, type, type, type]:
|
|
233
|
+
with _profile_section(profiler, "main.import.sdk_agent"):
|
|
234
|
+
from comate_agent_sdk.agent.compaction import CompactionConfig
|
|
235
|
+
from comate_agent_sdk.agent.options import AgentConfig
|
|
236
|
+
from comate_agent_sdk.agent.service import Agent
|
|
237
|
+
from comate_agent_sdk.context import EnvOptions
|
|
238
|
+
|
|
239
|
+
return Agent, AgentConfig, CompactionConfig, EnvOptions
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def _import_chat_session() -> type:
|
|
243
|
+
from comate_agent_sdk.agent.chat_session import ChatSession
|
|
244
|
+
|
|
245
|
+
return ChatSession
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def _import_print_mode_components() -> tuple[type, type]:
|
|
249
|
+
from comate_agent_sdk.agent.chat_session import ChatSession
|
|
250
|
+
from comate_agent_sdk.agent.events import TextEvent
|
|
251
|
+
|
|
252
|
+
return ChatSession, TextEvent
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def _import_rpc_bridge() -> type:
|
|
256
|
+
from comate_cli.terminal_agent.rpc_stdio import StdioRPCBridge
|
|
257
|
+
|
|
258
|
+
return StdioRPCBridge
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def _import_resume_selector():
|
|
262
|
+
from comate_cli.terminal_agent.resume_selector import select_resume_session_id
|
|
263
|
+
|
|
264
|
+
return select_resume_session_id
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def _import_interactive_tui_components(
|
|
268
|
+
profiler: StartupProfiler | None = None,
|
|
269
|
+
):
|
|
270
|
+
with _profile_section(profiler, "main.import.tui"):
|
|
271
|
+
from comate_cli.terminal_agent.event_renderer import EventRenderer
|
|
272
|
+
from comate_cli.terminal_agent.logging_adapter import setup_tui_logging
|
|
273
|
+
from comate_cli.terminal_agent.status_bar import StatusBar
|
|
274
|
+
from comate_cli.terminal_agent.tui import TerminalAgentTUI
|
|
275
|
+
|
|
276
|
+
return EventRenderer, setup_tui_logging, StatusBar, TerminalAgentTUI
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def _import_update_check_core():
|
|
280
|
+
from comate_cli.terminal_agent.update_check import (
|
|
281
|
+
check_update,
|
|
282
|
+
get_pending_update_prompt_info,
|
|
283
|
+
record_update_check_attempt,
|
|
284
|
+
should_check_for_update,
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
return (
|
|
288
|
+
check_update,
|
|
289
|
+
get_pending_update_prompt_info,
|
|
290
|
+
record_update_check_attempt,
|
|
291
|
+
should_check_for_update,
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def _import_update_prompt_components():
|
|
296
|
+
from comate_cli.terminal_agent.update_check import (
|
|
297
|
+
UpdatePromptChoice,
|
|
298
|
+
UpdatePromptDecision,
|
|
299
|
+
decide_update_prompt,
|
|
300
|
+
format_update_hint,
|
|
301
|
+
mark_update_seen,
|
|
302
|
+
record_skip_until_next_version,
|
|
303
|
+
run_update_command,
|
|
304
|
+
show_update_prompt,
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
return (
|
|
308
|
+
UpdatePromptChoice,
|
|
309
|
+
UpdatePromptDecision,
|
|
310
|
+
decide_update_prompt,
|
|
311
|
+
format_update_hint,
|
|
312
|
+
mark_update_seen,
|
|
313
|
+
record_skip_until_next_version,
|
|
314
|
+
run_update_command,
|
|
315
|
+
show_update_prompt,
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
|
|
201
319
|
async def _shutdown_session(session: ChatSession, *, label: str) -> None:
|
|
202
320
|
start = time.monotonic()
|
|
203
321
|
try:
|
|
@@ -235,17 +353,12 @@ async def _graceful_shutdown(*sessions: ChatSession) -> None:
|
|
|
235
353
|
logger.info(f"Graceful shutdown completed in {elapsed:.3f}s")
|
|
236
354
|
|
|
237
355
|
|
|
238
|
-
@tool("Add two numbers 涉及到加法运算 必须使用这个工具")
|
|
239
|
-
async def add(a: int, b: int) -> int:
|
|
240
|
-
return a + b
|
|
241
|
-
|
|
242
|
-
|
|
243
356
|
def _build_agent(
|
|
244
357
|
*,
|
|
245
358
|
project_root: Path | None = None,
|
|
246
359
|
profiler: StartupProfiler | None = None,
|
|
247
360
|
) -> Agent:
|
|
248
|
-
|
|
361
|
+
Agent, AgentConfig, CompactionConfig, EnvOptions = _import_sdk_agent_components(profiler)
|
|
249
362
|
from comate_cli.terminal_agent.config import store
|
|
250
363
|
|
|
251
364
|
resolved_project_root = project_root or _resolve_cli_project_root()
|
|
@@ -283,6 +396,7 @@ def _resolve_session(
|
|
|
283
396
|
cwd: Path | None = None,
|
|
284
397
|
profiler: StartupProfiler | None = None,
|
|
285
398
|
) -> tuple[ChatSession, str]:
|
|
399
|
+
ChatSession = _import_chat_session()
|
|
286
400
|
if resume_session_id:
|
|
287
401
|
if profiler is not None:
|
|
288
402
|
profiler.mark("session.resume.construct.start")
|
|
@@ -389,6 +503,7 @@ async def _run_print_mode(
|
|
|
389
503
|
*,
|
|
390
504
|
project_root: Path,
|
|
391
505
|
) -> None:
|
|
506
|
+
ChatSession, TextEvent = _import_print_mode_components()
|
|
392
507
|
session = ChatSession(agent, cwd=project_root, persistent=False)
|
|
393
508
|
try:
|
|
394
509
|
await _preload_mcp_in_tui(session)
|
|
@@ -452,12 +567,15 @@ async def run(
|
|
|
452
567
|
profiler = StartupProfiler.from_env(logger=logger, started_at=process_start_perf)
|
|
453
568
|
if app_import_start_perf is not None:
|
|
454
569
|
profiler.mark_at("main.app_import.start", app_import_start_perf)
|
|
570
|
+
profiler.mark_at("main.import.app.start", app_import_start_perf)
|
|
455
571
|
if app_import_done_perf is not None:
|
|
456
572
|
profiler.mark_at("main.app_import.done", app_import_done_perf)
|
|
573
|
+
profiler.mark_at("main.import.app.done", app_import_done_perf)
|
|
457
574
|
profiler.mark("cli_project_root.start")
|
|
458
575
|
project_root = _resolve_cli_project_root()
|
|
459
576
|
profiler.mark("cli_project_root.done")
|
|
460
577
|
profiler.mark("preflight.start")
|
|
578
|
+
run_preflight_if_needed = _import_preflight_runner()
|
|
461
579
|
preflight_result = await run_preflight_if_needed(
|
|
462
580
|
console=console,
|
|
463
581
|
project_root=project_root,
|
|
@@ -473,6 +591,12 @@ async def run(
|
|
|
473
591
|
print_logo(console, project_root=project_root)
|
|
474
592
|
profiler.mark("logo.printed")
|
|
475
593
|
|
|
594
|
+
(
|
|
595
|
+
_check_update_func,
|
|
596
|
+
get_pending_update_prompt_info,
|
|
597
|
+
_record_update_check_attempt,
|
|
598
|
+
_should_check_for_update,
|
|
599
|
+
) = _import_update_check_core()
|
|
476
600
|
pending_info = get_pending_update_prompt_info()
|
|
477
601
|
if pending_info is not None:
|
|
478
602
|
should_stop = await _handle_update_on_launch(pending_info)
|
|
@@ -494,6 +618,7 @@ async def run(
|
|
|
494
618
|
cwd=project_root,
|
|
495
619
|
profiler=profiler,
|
|
496
620
|
)
|
|
621
|
+
StdioRPCBridge = _import_rpc_bridge()
|
|
497
622
|
bridge = StdioRPCBridge(session)
|
|
498
623
|
try:
|
|
499
624
|
await bridge.run()
|
|
@@ -507,6 +632,7 @@ async def run(
|
|
|
507
632
|
return
|
|
508
633
|
|
|
509
634
|
if resume_select and not resume_session_id:
|
|
635
|
+
select_resume_session_id = _import_resume_selector()
|
|
510
636
|
selected_session_id = await select_resume_session_id(console, cwd=project_root)
|
|
511
637
|
if not selected_session_id:
|
|
512
638
|
return
|
|
@@ -515,8 +641,8 @@ async def run(
|
|
|
515
641
|
# 在 _resolve_session 前安装 TUILoggingHandler,确保 session 初始化期间
|
|
516
642
|
# 的 logger.warning()(如 user_instruction token 超限)能被 TUI 系统捕获,
|
|
517
643
|
# 而非 fallthrough 到 Python lastResort StreamHandler 以原始文本输出到 stderr。
|
|
644
|
+
EventRenderer, setup_tui_logging, StatusBar, TerminalAgentTUI = _import_interactive_tui_components(profiler)
|
|
518
645
|
renderer = EventRenderer(project_root=project_root)
|
|
519
|
-
from comate_cli.terminal_agent.logging_adapter import setup_tui_logging
|
|
520
646
|
profiler.mark("logging.setup.start")
|
|
521
647
|
logging_session = setup_tui_logging(renderer, project_root=project_root)
|
|
522
648
|
profiler.mark("logging.setup.done")
|
|
@@ -550,6 +676,12 @@ async def run(
|
|
|
550
676
|
profiler.mark("tui.init.done")
|
|
551
677
|
tui.add_resume_history(mode)
|
|
552
678
|
update_check_task: asyncio.Task[None] | None = None
|
|
679
|
+
(
|
|
680
|
+
_check_update_func,
|
|
681
|
+
_get_pending_update_prompt_info,
|
|
682
|
+
record_update_check_attempt,
|
|
683
|
+
should_check_for_update,
|
|
684
|
+
) = _import_update_check_core()
|
|
553
685
|
if should_check_for_update():
|
|
554
686
|
# attempt 语义:先记录尝试时间,确保 interval 内最多发起一次网络检查
|
|
555
687
|
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
|
)
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from copy import deepcopy
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any, Literal, Mapping, Sequence
|
|
8
|
+
|
|
9
|
+
from rich.console import Console
|
|
10
|
+
|
|
11
|
+
from comate_agent_sdk.agent.llm_levels import ALL_LEVELS
|
|
12
|
+
from comate_agent_sdk.agent.settings import (
|
|
13
|
+
USER_SETTINGS_PATH,
|
|
14
|
+
read_settings_json_object,
|
|
15
|
+
write_settings_json_atomic,
|
|
16
|
+
)
|
|
17
|
+
from comate_agent_sdk.facade.config.providers import (
|
|
18
|
+
PROVIDER_PRESETS,
|
|
19
|
+
LevelConfigDraft,
|
|
20
|
+
PreflightCheckResult,
|
|
21
|
+
ProviderPreset,
|
|
22
|
+
build_preview_payload,
|
|
23
|
+
evaluate_preflight_state,
|
|
24
|
+
get_provider_env_var,
|
|
25
|
+
merge_user_settings,
|
|
26
|
+
resolve_provider_presets,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
from comate_cli.terminal_agent.figures import BULLET_OPERATOR
|
|
30
|
+
from comate_cli.terminal_agent.config.store import (
|
|
31
|
+
apply_cli_config_snapshot_to_settings,
|
|
32
|
+
snapshot_from_settings_dict,
|
|
33
|
+
)
|
|
34
|
+
from comate_cli.terminal_agent.statusline.model import StatuslineConfig
|
|
35
|
+
from comate_cli.terminal_agent.statusline.store import build_statusline_settings
|
|
36
|
+
|
|
37
|
+
logger = logging.getLogger(__name__)
|
|
38
|
+
|
|
39
|
+
_LEVEL_LABEL_WIDTH = 6
|
|
40
|
+
_FIELD_LABEL_WIDTH = 9
|
|
41
|
+
_VALUE_MAX_WIDTH = 96
|
|
42
|
+
@dataclass(frozen=True, slots=True)
|
|
43
|
+
class PreflightResult:
|
|
44
|
+
status: Literal[
|
|
45
|
+
"skipped",
|
|
46
|
+
"configured",
|
|
47
|
+
"cancelled",
|
|
48
|
+
"blocked_non_interactive",
|
|
49
|
+
"failed",
|
|
50
|
+
]
|
|
51
|
+
detail: str | None = None
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
def should_abort_launch(self) -> bool:
|
|
55
|
+
return self.status in {"cancelled", "blocked_non_interactive", "failed"}
|
|
56
|
+
|
|
57
|
+
async def run_preflight_if_needed(
|
|
58
|
+
*,
|
|
59
|
+
console: Console,
|
|
60
|
+
project_root: Path,
|
|
61
|
+
interactive: bool,
|
|
62
|
+
provider_presets: Sequence[ProviderPreset] | None = None,
|
|
63
|
+
) -> PreflightResult:
|
|
64
|
+
check = evaluate_preflight_state(project_root=project_root)
|
|
65
|
+
if not check.needs_preflight:
|
|
66
|
+
return PreflightResult(status="skipped")
|
|
67
|
+
|
|
68
|
+
if not interactive:
|
|
69
|
+
_print_preflight_block_message(console=console, check=check)
|
|
70
|
+
return PreflightResult(status="blocked_non_interactive", detail="missing_llm_config")
|
|
71
|
+
|
|
72
|
+
from comate_cli.terminal_agent.preflight_wizard import _run_preflight_wizard
|
|
73
|
+
|
|
74
|
+
flow = await _run_preflight_wizard(
|
|
75
|
+
check=check,
|
|
76
|
+
project_root=project_root,
|
|
77
|
+
provider_presets=provider_presets,
|
|
78
|
+
)
|
|
79
|
+
if flow.cancel_detail is not None:
|
|
80
|
+
console.print("[yellow]Setup cancelled. Session was not started.[/]")
|
|
81
|
+
return PreflightResult(status="cancelled", detail=flow.cancel_detail)
|
|
82
|
+
draft_with_keys = flow.levels
|
|
83
|
+
if draft_with_keys is None:
|
|
84
|
+
console.print("[yellow]Setup cancelled. Session was not started.[/]")
|
|
85
|
+
return PreflightResult(status="cancelled", detail="unknown_cancelled")
|
|
86
|
+
|
|
87
|
+
settings_path = USER_SETTINGS_PATH.expanduser()
|
|
88
|
+
try:
|
|
89
|
+
merged = merge_user_settings(
|
|
90
|
+
existing=_load_user_settings_json(settings_path),
|
|
91
|
+
levels=draft_with_keys,
|
|
92
|
+
)
|
|
93
|
+
merged = _hydrate_cli_default_settings(merged)
|
|
94
|
+
write_user_settings_atomic(path=settings_path, data=merged)
|
|
95
|
+
except Exception as exc:
|
|
96
|
+
logger.exception("Failed to save pre-flight settings")
|
|
97
|
+
console.print(f"[red]Failed to save provider settings: {exc}[/]")
|
|
98
|
+
return PreflightResult(status="failed", detail="write_failed")
|
|
99
|
+
|
|
100
|
+
recheck = evaluate_preflight_state(project_root=project_root)
|
|
101
|
+
if recheck.needs_preflight:
|
|
102
|
+
console.print(
|
|
103
|
+
"[red]Settings were saved to user scope, but effective configuration is still incomplete. "
|
|
104
|
+
"Please check project/local overrides.[/]"
|
|
105
|
+
)
|
|
106
|
+
return PreflightResult(status="failed", detail="still_missing_after_write")
|
|
107
|
+
|
|
108
|
+
console.print(f"[green]Provider settings saved: {settings_path}[/]")
|
|
109
|
+
return PreflightResult(status="configured")
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def write_user_settings_atomic(*, path: Path, data: Mapping[str, Any]) -> None:
|
|
113
|
+
write_settings_json_atomic(path=path, data=data)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _load_user_settings_json(path: Path) -> dict[str, Any]:
|
|
117
|
+
return read_settings_json_object(path)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _deep_merge_missing_settings(
|
|
121
|
+
existing: Mapping[str, Any] | dict[str, Any],
|
|
122
|
+
defaults: Mapping[str, Any],
|
|
123
|
+
) -> dict[str, Any]:
|
|
124
|
+
result: dict[str, Any] = deepcopy(dict(existing))
|
|
125
|
+
for key, default_value in defaults.items():
|
|
126
|
+
current_value = result.get(key)
|
|
127
|
+
if key not in result:
|
|
128
|
+
result[key] = deepcopy(default_value)
|
|
129
|
+
elif isinstance(current_value, dict) and isinstance(default_value, dict):
|
|
130
|
+
result[key] = _deep_merge_missing_settings(current_value, default_value)
|
|
131
|
+
return result
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def _hydrate_cli_default_settings(data: Mapping[str, Any]) -> dict[str, Any]:
|
|
135
|
+
"""Fill missing CLI-owned defaults during settings writes.
|
|
136
|
+
|
|
137
|
+
This is write-path-only. `store.load()` remains read-only, so CLI startup
|
|
138
|
+
does not create ~/.agent/settings.json unless preflight or a picker saves.
|
|
139
|
+
"""
|
|
140
|
+
hydrated: dict[str, Any] = deepcopy(dict(data))
|
|
141
|
+
cli_raw = hydrated.get("comate_cli")
|
|
142
|
+
cli = deepcopy(cli_raw) if isinstance(cli_raw, dict) else {}
|
|
143
|
+
|
|
144
|
+
statusline_raw = cli.get("statusline")
|
|
145
|
+
if isinstance(statusline_raw, dict):
|
|
146
|
+
statusline_existing = statusline_raw
|
|
147
|
+
else:
|
|
148
|
+
legacy_statusline = hydrated.get("statusline")
|
|
149
|
+
statusline_existing = legacy_statusline if isinstance(legacy_statusline, dict) else {}
|
|
150
|
+
cli["statusline"] = _deep_merge_missing_settings(
|
|
151
|
+
statusline_existing,
|
|
152
|
+
build_statusline_settings(StatuslineConfig.default()),
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
hydrated["comate_cli"] = cli
|
|
156
|
+
hydrated.pop("statusline", None)
|
|
157
|
+
snapshot = snapshot_from_settings_dict(hydrated)
|
|
158
|
+
return apply_cli_config_snapshot_to_settings(hydrated, snapshot)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def _truncate_value(value: str, max_width: int = _VALUE_MAX_WIDTH) -> str:
|
|
162
|
+
text = value.strip()
|
|
163
|
+
if len(text) <= max_width:
|
|
164
|
+
return text
|
|
165
|
+
if max_width <= 3:
|
|
166
|
+
return text[:max_width]
|
|
167
|
+
return f"{text[:max_width - 3]}..."
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def _format_level_summary(*, level: str, entry: LevelConfigDraft) -> str:
|
|
171
|
+
model_value = _truncate_value(f"{entry.provider}:{entry.model}")
|
|
172
|
+
endpoint_value = _truncate_value(entry.base_url or "(default)")
|
|
173
|
+
level_cell = f"{level:<{_LEVEL_LABEL_WIDTH}}"
|
|
174
|
+
model_label = f"{'Model':<{_FIELD_LABEL_WIDTH}}"
|
|
175
|
+
endpoint_label = f"{'Endpoint':<{_FIELD_LABEL_WIDTH}}"
|
|
176
|
+
return (
|
|
177
|
+
f"{level_cell}{model_label}{model_value}\n"
|
|
178
|
+
f"{'':<{_LEVEL_LABEL_WIDTH}}{endpoint_label}{endpoint_value}"
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def _format_missing_reasons(reasons: tuple[str, ...], *, bullet_indent: str = " ") -> str:
|
|
183
|
+
if not reasons:
|
|
184
|
+
return "What's missing:\n - Unknown issue"
|
|
185
|
+
lines = ["What's missing:"]
|
|
186
|
+
lines.extend(f"{bullet_indent}- {reason}" for reason in reasons)
|
|
187
|
+
return "\n".join(lines)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def _print_preflight_block_message(*, console: Console, check: PreflightCheckResult) -> None:
|
|
191
|
+
reason_lines = _format_missing_reasons(check.reasons)
|
|
192
|
+
console.print("[red]Provider setup required[/]")
|
|
193
|
+
console.print("[dim]You need to configure an API provider before you can continue.[/]")
|
|
194
|
+
console.print(f"[dim]{reason_lines}[/]")
|
|
195
|
+
console.print("[dim]Run `comate` in interactive mode to complete provider setup.[/]")
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def _build_preflight_entry_options() -> list[dict[str, str]]:
|
|
199
|
+
return [
|
|
200
|
+
{
|
|
201
|
+
"value": "continue",
|
|
202
|
+
"label": "Start setup now",
|
|
203
|
+
"description": "",
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
"value": "cancel",
|
|
207
|
+
"label": "Exit",
|
|
208
|
+
"description": "",
|
|
209
|
+
},
|
|
210
|
+
]
|