comate-cli 0.8.6__tar.gz → 0.9.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (245) hide show
  1. {comate_cli-0.8.6 → comate_cli-0.9.0}/.gitignore +1 -0
  2. comate_cli-0.9.0/CHANGELOG.md +137 -0
  3. {comate_cli-0.8.6 → comate_cli-0.9.0}/PKG-INFO +1 -1
  4. comate_cli-0.9.0/comate_cli/distribution.py +83 -0
  5. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/main.py +67 -17
  6. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/mcp_cli.py +82 -24
  7. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/app.py +89 -18
  8. comate_cli-0.9.0/comate_cli/terminal_agent/builtin_commands/implement-plan.md +39 -0
  9. comate_cli-0.9.0/comate_cli/terminal_agent/builtin_commands/learn.md +100 -0
  10. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/custom_slash_commands.py +99 -19
  11. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/event_renderer.py +121 -5
  12. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/logo.py +38 -3
  13. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/preflight.py +15 -3
  14. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/question_view.py +2 -2
  15. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/status_bar.py +2 -0
  16. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/tool_result_formatters.py +7 -0
  17. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/tui.py +29 -3
  18. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/tui_parts/history_sync.py +27 -2
  19. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/tui_parts/render_panels.py +71 -0
  20. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/update_check.py +311 -61
  21. comate_cli-0.9.0/comate_cli/terminal_agent/update_messages.py +242 -0
  22. comate_cli-0.9.0/comate_cli/terminal_agent/windows_updater.py +254 -0
  23. {comate_cli-0.8.6 → comate_cli-0.9.0}/pyproject.toml +1 -1
  24. comate_cli-0.9.0/tests/test_app_profile_threading.py +244 -0
  25. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_custom_slash_commands.py +74 -0
  26. comate_cli-0.9.0/tests/test_distribution_contract.py +184 -0
  27. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_event_renderer_boundary.py +14 -10
  28. comate_cli-0.9.0/tests/test_event_renderer_stream_staging.py +186 -0
  29. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_event_renderer_streaming.py +79 -71
  30. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_event_renderer_tool_fold.py +3 -1
  31. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_history_sync.py +45 -0
  32. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_logo.py +115 -0
  33. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_main_args.py +147 -12
  34. comate_cli-0.9.0/tests/test_main_profile_entry.py +65 -0
  35. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_mcp_cli.py +232 -10
  36. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_preflight.py +57 -22
  37. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_question_view.py +6 -0
  38. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_skills_slash_command.py +42 -1
  39. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_status_bar.py +1 -0
  40. comate_cli-0.9.0/tests/test_stream_staging_panel.py +119 -0
  41. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_tool_result_formatters.py +19 -0
  42. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_update_check.py +647 -3
  43. comate_cli-0.9.0/tests/test_windows_updater.py +314 -0
  44. {comate_cli-0.8.6 → comate_cli-0.9.0}/uv.lock +2 -2
  45. comate_cli-0.8.6/CHANGELOG.md +0 -50
  46. {comate_cli-0.8.6 → comate_cli-0.9.0}/CONTEXT.md +0 -0
  47. {comate_cli-0.8.6 → comate_cli-0.9.0}/README.md +0 -0
  48. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/__init__.py +0 -0
  49. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/__main__.py +0 -0
  50. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/__init__.py +0 -0
  51. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/animations.py +0 -0
  52. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/assistant_render.py +0 -0
  53. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/builtin_commands/.keep +0 -0
  54. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/codenames.py +0 -0
  55. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/config/__init__.py +0 -0
  56. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/config/model.py +0 -0
  57. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/config/picker.py +0 -0
  58. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/config/picker_state.py +0 -0
  59. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/config/store.py +0 -0
  60. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/env_utils.py +0 -0
  61. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/error_display.py +0 -0
  62. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/figures.py +0 -0
  63. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/file_ref_hint.py +0 -0
  64. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/fragment_utils.py +0 -0
  65. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/goal_resume_view.py +0 -0
  66. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/history_printer.py +0 -0
  67. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/input_geometry.py +0 -0
  68. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/layout_coordinator.py +0 -0
  69. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/logging_adapter.py +0 -0
  70. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/markdown_render.py +0 -0
  71. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/mention_completer.py +0 -0
  72. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/message_style.py +0 -0
  73. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/models.py +0 -0
  74. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/path_context_hint.py +0 -0
  75. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/plugins/__init__.py +0 -0
  76. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/plugins/components/__init__.py +0 -0
  77. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/plugins/components/detail_view.py +0 -0
  78. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/plugins/components/plugin_list.py +0 -0
  79. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/plugins/components/search_box.py +0 -0
  80. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/plugins/components/tab_bar.py +0 -0
  81. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/plugins/marketplace_install_view.py +0 -0
  82. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/plugins/operation_state.py +0 -0
  83. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/plugins/plugin_picker.py +0 -0
  84. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/plugins/tabs/__init__.py +0 -0
  85. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/plugins/tabs/discover_tab.py +0 -0
  86. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/plugins/tabs/errors_tab.py +0 -0
  87. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/plugins/tabs/installed_tab.py +0 -0
  88. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/plugins/tabs/marketplaces_tab.py +0 -0
  89. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/preflight_wizard.py +0 -0
  90. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/print_mode.py +0 -0
  91. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/recap/__init__.py +0 -0
  92. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/recap/controller.py +0 -0
  93. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/resume_picker.py +0 -0
  94. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/resume_preview.py +0 -0
  95. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/resume_selector.py +0 -0
  96. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/rpc_protocol.py +0 -0
  97. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/rpc_stdio.py +0 -0
  98. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/selection_menu.py +0 -0
  99. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/slash_commands.py +0 -0
  100. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/startup.py +0 -0
  101. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/startup_profile.py +0 -0
  102. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/statusline/__init__.py +0 -0
  103. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/statusline/model.py +0 -0
  104. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/statusline/picker.py +0 -0
  105. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/statusline/picker_state.py +0 -0
  106. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/statusline/store.py +0 -0
  107. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/text_effects.py +0 -0
  108. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/tips.py +0 -0
  109. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/tool_fold.py +0 -0
  110. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/tool_result_store.py +0 -0
  111. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/tool_result_viewer.py +0 -0
  112. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/tool_view.py +0 -0
  113. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/transcript_viewer.py +0 -0
  114. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/tui_parts/__init__.py +0 -0
  115. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/tui_parts/btw_view.py +0 -0
  116. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/tui_parts/commands.py +0 -0
  117. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/tui_parts/input_behavior.py +0 -0
  118. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/tui_parts/key_bindings.py +0 -0
  119. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/tui_parts/mcp_connecting_view.py +0 -0
  120. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/tui_parts/slash_command_registry.py +0 -0
  121. {comate_cli-0.8.6 → comate_cli-0.9.0}/comate_cli/terminal_agent/tui_parts/ui_mode.py +0 -0
  122. {comate_cli-0.8.6 → comate_cli-0.9.0}/dist-comate/.gitignore +0 -0
  123. {comate_cli-0.8.6 → comate_cli-0.9.0}/dist-comate/comate_cli-0.7.10-py3-none-any.whl +0 -0
  124. {comate_cli-0.8.6 → comate_cli-0.9.0}/dist-comate/comate_cli-0.7.10.tar.gz +0 -0
  125. {comate_cli-0.8.6 → comate_cli-0.9.0}/dist-hico/.gitignore +0 -0
  126. {comate_cli-0.8.6 → comate_cli-0.9.0}/dist-hico/hico_cli-0.7.17-py3-none-any.whl +0 -0
  127. {comate_cli-0.8.6 → comate_cli-0.9.0}/dist-hico/hico_cli-0.7.17.tar.gz +0 -0
  128. {comate_cli-0.8.6 → comate_cli-0.9.0}/docs/cli-rpc-stdio-guide.md +0 -0
  129. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/config/__init__.py +0 -0
  130. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/config/test_model.py +0 -0
  131. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/config/test_picker_state.py +0 -0
  132. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/config/test_picker_ui.py +0 -0
  133. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/config/test_roundtrip.py +0 -0
  134. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/config/test_store_load.py +0 -0
  135. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/config/test_store_save.py +0 -0
  136. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/conftest.py +0 -0
  137. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/fixtures/fake_mcp_misbehaving_stdout.py +0 -0
  138. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/recap/test_idle_recap_controller.py +0 -0
  139. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/recap/test_recap_history_rendering.py +0 -0
  140. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/recap/test_tui_idle_recap_integration.py +0 -0
  141. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/statusline/__init__.py +0 -0
  142. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/statusline/test_model.py +0 -0
  143. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/statusline/test_picker_state.py +0 -0
  144. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/statusline/test_store.py +0 -0
  145. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_animator_shuffle.py +0 -0
  146. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_app_mcp_preload.py +0 -0
  147. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_app_preflight_gate.py +0 -0
  148. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_app_print_mode.py +0 -0
  149. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_app_shutdown.py +0 -0
  150. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_app_startup_latency.py +0 -0
  151. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_app_token_cost_config.py +0 -0
  152. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_app_usage_line.py +0 -0
  153. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_btw_slash_command.py +0 -0
  154. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_cli_project_root.py +0 -0
  155. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_compact_command_semantics.py +0 -0
  156. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_completion_context_activation.py +0 -0
  157. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_completion_status_panel.py +0 -0
  158. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_context_command.py +0 -0
  159. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_discover_tab.py +0 -0
  160. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_errors_tab.py +0 -0
  161. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_event_renderer.py +0 -0
  162. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_event_renderer_e2e.py +0 -0
  163. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_event_renderer_log_boundary.py +0 -0
  164. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_event_renderer_log_queue.py +0 -0
  165. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_event_renderer_tool_args_progress.py +0 -0
  166. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_file_ref_hint.py +0 -0
  167. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_format_error.py +0 -0
  168. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_goal_resume_tui.py +0 -0
  169. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_goal_resume_view.py +0 -0
  170. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_goal_slash_command.py +0 -0
  171. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_handle_error.py +0 -0
  172. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_history_printer.py +0 -0
  173. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_history_printer_log.py +0 -0
  174. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_history_printer_subtitle_position.py +0 -0
  175. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_history_printer_tool_fold.py +0 -0
  176. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_history_sync_tool_fold.py +0 -0
  177. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_input_behavior.py +0 -0
  178. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_input_history.py +0 -0
  179. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_installed_tab.py +0 -0
  180. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_interrupt_exit_semantics.py +0 -0
  181. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_layout_coordinator.py +0 -0
  182. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_logging_adapter.py +0 -0
  183. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_markdown_render.py +0 -0
  184. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_marketplaces_tab.py +0 -0
  185. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_mcp_slash_command.py +0 -0
  186. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_mention_completer.py +0 -0
  187. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_model_switch_command.py +0 -0
  188. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_path_context_hint.py +0 -0
  189. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_plugin_operation_state.py +0 -0
  190. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_plugin_slash_commands.py +0 -0
  191. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_plugin_tui_components.py +0 -0
  192. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_preflight_copilot.py +0 -0
  193. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_question_key_bindings.py +0 -0
  194. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_resume_picker.py +0 -0
  195. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_resume_preview.py +0 -0
  196. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_resume_selector.py +0 -0
  197. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_rewind_command_semantics.py +0 -0
  198. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_rpc_protocol.py +0 -0
  199. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_rpc_stdio_bridge.py +0 -0
  200. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_selection_menu.py +0 -0
  201. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_session_query_token_summary.py +0 -0
  202. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_shutdown_noise_guard.py +0 -0
  203. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_shutdown_noise_integration.py +0 -0
  204. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_slash_argument_hint.py +0 -0
  205. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_slash_clear.py +0 -0
  206. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_slash_completer.py +0 -0
  207. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_slash_registry.py +0 -0
  208. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_startup_import_budget.py +0 -0
  209. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_startup_profile.py +0 -0
  210. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_status_bar_transient.py +0 -0
  211. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_task_panel_format.py +0 -0
  212. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_task_panel_key_bindings.py +0 -0
  213. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_task_panel_rendering.py +0 -0
  214. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_task_poll.py +0 -0
  215. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_tips.py +0 -0
  216. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_tool_fold.py +0 -0
  217. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_tool_fold_panel.py +0 -0
  218. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_tool_result_store.py +0 -0
  219. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_tool_result_viewer.py +0 -0
  220. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_tool_result_viewer_key_bindings.py +0 -0
  221. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_tool_view.py +0 -0
  222. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_transcript_viewer.py +0 -0
  223. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_transcript_viewer_tool_fold.py +0 -0
  224. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_tui_builtin_slash_chain.py +0 -0
  225. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_tui_elapsed_status.py +0 -0
  226. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_tui_esc_queue.py +0 -0
  227. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_tui_llm_retry_panel.py +0 -0
  228. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_tui_local_interactive_barrier.py +0 -0
  229. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_tui_mcp_init_gate.py +0 -0
  230. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_tui_paste_newline_guard.py +0 -0
  231. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_tui_paste_placeholder.py +0 -0
  232. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_tui_provider_error_shield.py +0 -0
  233. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_tui_queue_preview.py +0 -0
  234. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_tui_queue_sdk_source.py +0 -0
  235. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_tui_scheduled_fire_log.py +0 -0
  236. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_tui_send_undo_window.py +0 -0
  237. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_tui_split_invariance.py +0 -0
  238. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_tui_startup_latency.py +0 -0
  239. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_tui_team_messages.py +0 -0
  240. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_tui_thinking_display.py +0 -0
  241. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_tui_tool_result_registry_lifecycle.py +0 -0
  242. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_usage_command.py +0 -0
  243. {comate_cli-0.8.6 → comate_cli-0.9.0}/tests/test_wrap_user_text.py +0 -0
  244. {comate_cli-0.8.6 → comate_cli-0.9.0}/tools/logo_lab.py +0 -0
  245. {comate_cli-0.8.6 → comate_cli-0.9.0}/tools/nico_preview.py +0 -0
@@ -18,6 +18,7 @@ claude_code_codebase/
18
18
  build/
19
19
  develop-eggs/
20
20
  dist/
21
+ packages/comate_cli/dist-*/
21
22
  downloads/
22
23
  eggs/
23
24
  .eggs/
@@ -0,0 +1,137 @@
1
+ # Changelog
2
+
3
+ ## Unreleased
4
+
5
+ ### Fixed
6
+
7
+ - Windows 上 Ctrl+C 退出时 `ERROR:mcp.client.stdio:Failed to parse JSONRPC message from server`
8
+ 和 traceback 漏到终端的问题。修复包含两部分:`app.py` finally 顺序调整,
9
+ 以及 SDK stdio MCP 关闭路径切换到主动信号升级链。
10
+ - Windows 上 Ctrl+C 退出时 `Exception ignored in: <BaseSubprocessTransport.__del__>`
11
+ 和 `ValueError: I/O operation on closed pipe` 漏到终端的问题。现在由
12
+ `_ShutdownNoiseGuard` catch-all 接管 `sys.unraisablehook` 并写入 agent log。
13
+
14
+ ### Added
15
+
16
+ - 新增 `/clear` slash command:清空当前 leader session 的 conversation,
17
+ 保留 session_id、team membership、inbox 与 TaskStore。成功后会清屏、
18
+ 重打 logo、重置 TUI transcript view,并刷新 status bar;busy 或
19
+ compacting 状态会拒绝执行。
20
+ - `_ShutdownNoiseGuard` 扩展为四合一接管器:SIGINT、`sys.unraisablehook`、
21
+ `sys.excepthook`、`warnings.showwarning`。捕获到的噪音统一路由到独立的
22
+ `comate.noise` logger(`propagate=False`),写入 `~/.comate/logs/agent.log`。
23
+ - 新增 `COMATE_DEBUG_NOISE=1` 环境变量逃生口,开发排障时可关闭噪音接管器,
24
+ 查看旧版本的原始 stderr 输出。
25
+
26
+ ### Changed
27
+
28
+ - TUI 分支退出时先执行 `_graceful_shutdown(...)`,再关闭 `logging_session`,
29
+ 让现有 logging 屏蔽机制覆盖 MCP teardown 全程。
30
+
31
+ ## 0.9.0 - 2026-07-02
32
+
33
+ ### Added
34
+
35
+ - 新增 `DistributionProfile` 白标扩展面,调用方可注入产品名、命令名、包名、
36
+ system role、provider presets、logo renderer、更新源与安装参数。
37
+ - CLI 入口、preflight、logo、更新检查、resume hint、MCP 子命令帮助文案等运行时路径
38
+ 支持通过发行版 profile 接管用户可见身份。
39
+
40
+ ### Changed
41
+
42
+ - `UpdateTarget` 改为由 `fetch_latest` 回调和 `install_args` 描述更新源。
43
+ 直接构造 `UpdateTarget` 的下游需要从旧字段迁移到新契约;这是对该内部扩展面的
44
+ 破坏性变更。
45
+ - 更新命令现在会透传 profile 提供的安装参数,支持发行版自定义安装源。
46
+
47
+ ### Removed
48
+
49
+ - 公开仓库中的发行版专用示例、测试夹具和历史文档已中性化或移出仓库;构建产物目录
50
+ `packages/comate_cli/dist-*/` 已加入忽略规则。
51
+
52
+ ## 0.8.6 - 2026-06-24
53
+
54
+ ### `/loop` 定时循环能力升级
55
+
56
+ 本周期对 `/loop` 做了一次提示词驱动的重构,修复了一个影响中文用户的核心 bug,
57
+ 并收紧了调度规则。
58
+
59
+ #### Fixed
60
+
61
+ - **中文「每隔N分钟」不再静默回退为 10 分钟**:之前 `/loop 每隔1分钟 检查构建状态`
62
+ 这类中文自然语言措辞,会因为未命中代码里的英文正则而静默回退为默认 10 分钟,
63
+ 调度完全不符合预期。现在间隔解析从"代码硬正则"改为提示词驱动,把规则和原始输入
64
+ 交给模型按用户意图(任意语言)自行解析。中文「每隔1分钟」「每两小时」、英文
65
+ "every couple of hours" 都能被正确识别。
66
+ - **`/loop` 运行数次后神秘停止**:根因是 TUI 状态栏刷新抛异常时会"杀死" event pump,
67
+ 进而关闭 scheduled prompt scheduler。现在状态栏刷新失败被隔离为局部防护(带日志),
68
+ 不再波及 event pump 与 `/loop` 调度消费。
69
+
70
+ #### Changed
71
+
72
+ - **间隔→cron 转换规则收紧**:为避免 cron 在边界重置导致节奏不均匀,规则现在只允许
73
+ 能整除单位的间隔,其他值会自动向上取整并明确告知取整结果:
74
+ - 分钟(能整除 60):1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30(如 `7m → 10m`)
75
+ - 小时(能整除 24):1, 2, 3, 4, 6, 8, 12(如 `5h → 6h`;13~24h → `1d`)
76
+ - 天:仅 `1d`(禁止多天,`Nd, N>1` 会在月末重置,无法表达"每 N 天")
77
+ - 显式禁止 `7m`、`5h`、`2d` 等会让节奏"漂移"的写法。
78
+ - **CronCreate 使用规则精简**:删除四条容易让模型误判触发窗口/续跑语义的规则段落,
79
+ 追加 recurring 与 one-shot 的可复用 cron 示例模板,减少模型凭空构造错误 cron。
80
+
81
+ ### Added
82
+
83
+ - **空闲 Recap(会话摘要回顾)**:回到终端时自动生成"离开期间发生了什么"摘要并渲染到
84
+ scrollback,带 label 标签与禁用提示,方便恢复上下文。
85
+ - **`/learn` slash 命令**:引导把刚解决的问题沉淀为可复用知识。
86
+ - **`/implement-plan` slash 命令**:一键进入计划执行模式。
87
+ - **内置 default custom commands 加载链**:开箱即用一组默认 custom slash 命令。
88
+ - **工具调用流式进度**:Loading 行实时展示工具参数解析进度(token 估算),告别"干等"。
89
+ - **LLM 重试动态面板**:主 agent LLM 重试状态可视化,底部面板动态展示重试进度,SDK
90
+ 原始重试噪声收敛到统一面板。
91
+ - **`Ctrl+J` 手动换行**:多行输入时显式插入换行。
92
+ - **`@path` 提示行统一**:抽出为单一事实源并在 staging 界面同步显示。
93
+ - **Logo 支持 2x 放大**;统一圆点 marker 为呼吸点动画;tool signature 路径自动相对化。
94
+
95
+ ### Changed(体验优化)
96
+
97
+ - 退出汇总输出精简、token 字段标签重命名。
98
+ - 时长显示数字与单位间加空格(如 `12 s`)。
99
+ - scrollback diff 展示优化。
100
+ - 错误 tool result 截断/展开提示保持 dim 样式。
101
+
102
+ ### Fixed(稳定性)
103
+
104
+ - **Windows 状态栏 git 命令卡住 TUI 事件泵**。
105
+ - **provider 异常击穿 prompt_toolkit 防护**:LLM provider 抛出未处理异常时不再击穿到 TUI 层。
106
+ - **LLM stream 清理的无界等待**。
107
+ - **启动更新检查改为真正异步 HTTP**:不再占用默认线程池导致退出卡死;移除 24h 限频机制。
108
+ - **memory prefetch 退出噪声**兜底屏蔽。
109
+
110
+ ### Fixed(渲染与配置)
111
+
112
+ - scrollback diff 背景未铺满内容行、diff 背景在 tab 行上的宽度计算错误。
113
+ - 代码块末行背景填充保留,避免渲染断裂。
114
+ - Bash scrollback 命令摘要单行化,避免多行折叠错乱。
115
+ - marketplace 详情页更新反馈不正确。
116
+ - 移除 CLI 的上下文压缩阈值配置(改由 SDK 安全窗口自动管理)。
117
+
118
+ ## 0.7.0a1 - 2026-05-19 (alpha)
119
+
120
+ > 这是一个 **alpha 预发布版本**,用于内部 / 早期测试。
121
+ > `pip install comate-cli` 默认不会安装到本版本,需要显式 `pip install comate-cli==0.7.0a1` 或 `pip install --pre comate-cli`。
122
+
123
+ ### Breaking Changes
124
+
125
+ - 依赖 `comate-agent-sdk>=0.8.0a1`,与旧 SDK 不兼容。
126
+ - 旧版本生成的 resume jsonl / context 快照不保证可以正常回放,**建议从空 session 重新开始**。
127
+
128
+ ### 累积变更
129
+
130
+ 自 0.6.x 起累积大量 TUI 与会话能力改进,涵盖:
131
+
132
+ - Resume picker 重做:搜索、跨 cwd 切换、预览模式、视觉抛光
133
+ - Auto compact 期间显式提示 `Compacting context`
134
+ - Team / subagent 事件透出与渲染统一
135
+ - 与 SDK 0.8.0a2 配套的 tool envelope / token 账本展示
136
+
137
+ 详细变更请参考 git log。
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: comate-cli
3
- Version: 0.8.6
3
+ Version: 0.9.0
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
@@ -0,0 +1,83 @@
1
+ """Distribution profile contract for whitelabel CLI packages.
2
+
3
+ Private overlay packages can construct a DistributionProfile and pass it to
4
+ comate_cli.main.main(profile=...) to customize branding, presets, update
5
+ sources, logo rendering, and the system role. This module is the only public
6
+ contract for that extension surface.
7
+
8
+ Design:
9
+ docs/superpowers/specs/2026-07-02-cli-distribution-whitelabel-design.md
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ from dataclasses import dataclass
15
+ from pathlib import Path
16
+ from typing import TYPE_CHECKING, Awaitable, Callable, Protocol
17
+
18
+ if TYPE_CHECKING:
19
+ from comate_agent_sdk.facade.config.provider_presets import ProviderPreset
20
+ from rich.console import Console
21
+
22
+ REQUIRED_PROVIDER_LEVELS: tuple[str, ...] = ("LOW", "MID", "HIGH")
23
+
24
+
25
+ @dataclass(frozen=True, slots=True)
26
+ class LatestRelease:
27
+ """Result returned by an update source."""
28
+
29
+ version: str
30
+ release_notes_url: str | None = None
31
+
32
+
33
+ LatestVersionFetcher = Callable[[], Awaitable["LatestRelease | None"]]
34
+
35
+
36
+ @dataclass(frozen=True, slots=True)
37
+ class UpdateSource:
38
+ fetch_latest: LatestVersionFetcher
39
+ install_args: tuple[str, ...] = ()
40
+ release_notes_url: str | None = None
41
+
42
+ def __post_init__(self) -> None:
43
+ if not callable(self.fetch_latest):
44
+ raise ValueError("UpdateSource.fetch_latest must be callable")
45
+ object.__setattr__(self, "install_args", tuple(self.install_args))
46
+
47
+
48
+ class LogoRenderer(Protocol):
49
+ def __call__(self, console: "Console", *, project_root: Path | None = None) -> None: ...
50
+
51
+
52
+ @dataclass(frozen=True, slots=True)
53
+ class DistributionProfile:
54
+ product_name: str = "Comate"
55
+ command_name: str = "comate"
56
+ package_name: str = "comate-cli"
57
+ provider_presets: tuple["ProviderPreset", ...] | None = None
58
+ update: UpdateSource | None = None
59
+ render_logo: LogoRenderer | None = None
60
+ system_role: str | None = None
61
+
62
+ def __post_init__(self) -> None:
63
+ for field_name in ("product_name", "command_name", "package_name"):
64
+ if not str(getattr(self, field_name)).strip():
65
+ raise ValueError(f"DistributionProfile.{field_name} must be non-empty")
66
+
67
+ if self.provider_presets is None:
68
+ return
69
+
70
+ object.__setattr__(self, "provider_presets", tuple(self.provider_presets))
71
+
72
+ if not self.provider_presets:
73
+ raise ValueError("DistributionProfile.provider_presets must be non-empty when provided")
74
+
75
+ for preset in self.provider_presets:
76
+ missing = [level for level in REQUIRED_PROVIDER_LEVELS if level not in preset.models]
77
+ if missing:
78
+ raise ValueError(
79
+ f"provider preset {preset.preset_id!r} missing levels: {', '.join(missing)}"
80
+ )
81
+
82
+
83
+ DEFAULT_PROFILE = DistributionProfile()
@@ -5,6 +5,7 @@ import asyncio
5
5
  import logging
6
6
  import os
7
7
  import signal
8
+ from pathlib import Path
8
9
  import subprocess
9
10
  import sys
10
11
  import time
@@ -32,7 +33,7 @@ PRINT_OUTPUT_FORMAT_JSON = "json"
32
33
  PRINT_OUTPUT_FORMATS = frozenset({PRINT_OUTPUT_FORMAT_TEXT, PRINT_OUTPUT_FORMAT_JSON})
33
34
  PRINT_OUTPUT_FORMAT_UNSUPPORTED = frozenset({"stream-json"})
34
35
 
35
- ParsedArgs = tuple[bool, str | None, bool, str | None, str]
36
+ ParsedArgs = tuple[bool, str | None, bool, str | None, str, tuple[str, ...]]
36
37
 
37
38
 
38
39
  class _TerminalStateGuard:
@@ -205,13 +206,17 @@ class _ShutdownNoiseGuard:
205
206
  return log
206
207
 
207
208
 
208
- def _usage_text() -> str:
209
+ def _usage_text(command_name: str = "comate") -> str:
209
210
  return (
210
211
  "Usage:\n"
211
- " comate [--rpc-stdio]\n"
212
- " comate -p [--output-format text|json] <prompt>\n"
213
- " comate resume [<session_id>] [--rpc-stdio]\n"
214
- " comate mcp <subcommand> [options]"
212
+ f" {command_name} [--rpc-stdio] [--add-dir <path>]...\n"
213
+ f" {command_name} -p [--output-format text|json] <prompt>\n"
214
+ f" {command_name} resume [<session_id>] [--rpc-stdio]\n"
215
+ f" {command_name} mcp <subcommand> [options]\n"
216
+ "\n"
217
+ "Options:\n"
218
+ " --add-dir <path> Grant tool read/write access to an extra directory\n"
219
+ " (repeatable). Applies to subagents too."
215
220
  )
216
221
 
217
222
 
@@ -221,6 +226,7 @@ def _parse_args(argv: list[str]) -> ParsedArgs:
221
226
  output_format = PRINT_OUTPUT_FORMAT_TEXT
222
227
  output_format_explicit = False
223
228
  positionals: list[str] = []
229
+ add_dirs: list[str] = []
224
230
 
225
231
  index = 0
226
232
  while index < len(argv):
@@ -253,6 +259,19 @@ def _parse_args(argv: list[str]) -> ParsedArgs:
253
259
  output_format = value
254
260
  index += 1
255
261
  continue
262
+ if arg == "--add-dir" or arg.startswith("--add-dir="):
263
+ if arg == "--add-dir":
264
+ index += 1
265
+ if index >= len(argv):
266
+ raise _ArgumentError("--add-dir requires a path")
267
+ value = argv[index]
268
+ else:
269
+ value = arg.partition("=")[2]
270
+ if not value:
271
+ raise _ArgumentError("--add-dir requires a path")
272
+ add_dirs.append(value)
273
+ index += 1
274
+ continue
256
275
  if arg.startswith("-"):
257
276
  raise _ArgumentError(f"Unknown option: {arg}")
258
277
  positionals.append(arg)
@@ -266,11 +285,11 @@ def _parse_args(argv: list[str]) -> ParsedArgs:
266
285
  if rpc_stdio:
267
286
  raise _ArgumentError("-p/--print and --rpc-stdio are mutually exclusive")
268
287
  print_prompt = " ".join(positionals) if positionals else ""
269
- return rpc_stdio, None, False, print_prompt, output_format
288
+ return rpc_stdio, None, False, print_prompt, output_format, tuple(add_dirs)
270
289
 
271
290
  # Non -p mode: original logic
272
291
  if not positionals:
273
- return rpc_stdio, None, False, None, output_format
292
+ return rpc_stdio, None, False, None, output_format, tuple(add_dirs)
274
293
 
275
294
  command = positionals[0]
276
295
  if command != "resume":
@@ -281,14 +300,33 @@ def _parse_args(argv: list[str]) -> ParsedArgs:
281
300
  raise _ArgumentError(
282
301
  "resume without <session_id> does not support --rpc-stdio"
283
302
  )
284
- return rpc_stdio, None, True, None, output_format
303
+ return rpc_stdio, None, True, None, output_format, tuple(add_dirs)
285
304
 
286
305
  if len(positionals) == 2:
287
- return rpc_stdio, positionals[1], False, None, output_format
306
+ return rpc_stdio, positionals[1], False, None, output_format, tuple(add_dirs)
288
307
 
289
308
  raise _ArgumentError("resume accepts at most one <session_id>")
290
309
 
291
310
 
311
+ def _validate_add_dirs(raw_dirs: tuple[str, ...]) -> tuple[Path, ...]:
312
+ """Resolve and validate --add-dir paths (must exist and be directories).
313
+
314
+ Raises _ArgumentError (fail-fast) so a typo'd directory never silently
315
+ grants nothing: a granted dir is a permission boundary, so we reject
316
+ rather than skip. Sensitive/over-broad roots are left to the SDK path
317
+ guard at runtime.
318
+ """
319
+ resolved: list[Path] = []
320
+ for raw in raw_dirs:
321
+ path = Path(raw).expanduser().resolve()
322
+ if not path.exists():
323
+ raise _ArgumentError(f"--add-dir path does not exist: {raw}")
324
+ if not path.is_dir():
325
+ raise _ArgumentError(f"--add-dir path is not a directory: {raw}")
326
+ resolved.append(path)
327
+ return tuple(resolved)
328
+
329
+
292
330
  def _read_stdin_posix_idle_drain(
293
331
  stdin: object,
294
332
  *,
@@ -350,18 +388,22 @@ def _read_stdin_if_ready(
350
388
  return str(read()) if callable(read) else ""
351
389
 
352
390
 
353
- def main(argv: list[str] | None = None) -> None:
391
+ def main(argv: list[str] | None = None, *, profile=None) -> None:
392
+ from comate_cli.distribution import DEFAULT_PROFILE
393
+
394
+ profile = profile or DEFAULT_PROFILE
395
+ command_name = profile.command_name
354
396
  run_argv = list(argv) if argv is not None else sys.argv[1:]
355
397
 
356
398
  if run_argv in (["--help"], ["-h"]):
357
- sys.stdout.write(f"{_usage_text()}\n")
399
+ sys.stdout.write(f"{_usage_text(command_name=command_name)}\n")
358
400
  return
359
401
 
360
402
  if run_argv and run_argv[0] == "mcp":
361
403
  from comate_cli.mcp_cli import McpCliError, run_mcp_command
362
404
 
363
405
  try:
364
- run_mcp_command(run_argv[1:])
406
+ run_mcp_command(run_argv[1:], command_name=command_name)
365
407
  return
366
408
  except McpCliError as exc:
367
409
  sys.stderr.write(f"{exc}\n")
@@ -379,11 +421,17 @@ def main(argv: list[str] | None = None) -> None:
379
421
  _app_import_done_perf = time.perf_counter()
380
422
 
381
423
  try:
382
- rpc_stdio, resume_session_id, resume_select, print_prompt, print_output_format = (
383
- _parse_args(run_argv)
384
- )
424
+ (
425
+ rpc_stdio,
426
+ resume_session_id,
427
+ resume_select,
428
+ print_prompt,
429
+ print_output_format,
430
+ add_dirs_raw,
431
+ ) = _parse_args(run_argv)
432
+ add_dirs = _validate_add_dirs(add_dirs_raw)
385
433
  except _ArgumentError as exc:
386
- sys.stderr.write(f"{exc}\n{_usage_text()}\n")
434
+ sys.stderr.write(f"{exc}\n{_usage_text(command_name=command_name)}\n")
387
435
  raise SystemExit(2) from exc
388
436
 
389
437
  # Assemble print mode message
@@ -404,9 +452,11 @@ def main(argv: list[str] | None = None) -> None:
404
452
  resume_select=resume_select,
405
453
  print_message=print_message,
406
454
  print_output_format=print_output_format,
455
+ add_dirs=add_dirs,
407
456
  process_start_perf=_PROCESS_START_PERF,
408
457
  app_import_start_perf=_app_import_start_perf,
409
458
  app_import_done_perf=_app_import_done_perf,
459
+ profile=profile,
410
460
  )
411
461
  )
412
462
  except KeyboardInterrupt:
@@ -17,6 +17,7 @@ from comate_agent_sdk.mcp import (
17
17
  validate_server_config,
18
18
  write_mcp_servers_to_path,
19
19
  )
20
+ from comate_agent_sdk.mcp.cwd import format_stdio_cwd_resolution, resolve_stdio_cwd
20
21
  from comate_agent_sdk.mcp.types import McpServerConfig
21
22
  from comate_agent_sdk.utils.paths import PathInput
22
23
 
@@ -40,27 +41,27 @@ class _McpArgumentParser(argparse.ArgumentParser):
40
41
  raise McpCliError(message)
41
42
 
42
43
 
43
- def _build_parser() -> argparse.ArgumentParser:
44
+ def _build_parser(command_name: str = "comate") -> argparse.ArgumentParser:
44
45
  parser = _McpArgumentParser(
45
- prog="comate mcp",
46
+ prog=f"{command_name} mcp",
46
47
  description="Configure and manage MCP servers",
47
48
  formatter_class=argparse.RawDescriptionHelpFormatter,
48
49
  epilog=(
49
50
  "Examples:\n"
50
51
  " # Add HTTP server:\n"
51
- " comate mcp add --transport http sentry https://mcp.sentry.dev/mcp\n\n"
52
+ f" {command_name} mcp add --transport http sentry https://mcp.sentry.dev/mcp\n\n"
52
53
  " # Add HTTP server with headers:\n"
53
- " comate mcp add --transport http corridor https://app.corridor.dev/api/mcp "
54
+ f" {command_name} mcp add --transport http corridor https://app.corridor.dev/api/mcp "
54
55
  '--header "Authorization: Bearer ..."\n\n'
55
56
  " # Add stdio server with environment variables:\n"
56
- " comate mcp add -e API_KEY=xxx my-server -- npx my-mcp-server\n\n"
57
+ f" {command_name} mcp add -e API_KEY=xxx my-server -- npx my-mcp-server\n\n"
57
58
  " # Add stdio server with subprocess flags:\n"
58
- " comate mcp add my-server -- my-command --some-flag arg1"
59
+ f" {command_name} mcp add my-server -- my-command --some-flag arg1"
59
60
  ),
60
61
  )
61
62
  subparsers = parser.add_subparsers(dest="command")
62
63
 
63
- add_parser = subparsers.add_parser("add", help="Add an MCP server to comate")
64
+ add_parser = subparsers.add_parser("add", help=f"Add an MCP server to {command_name}")
64
65
  add_parser.add_argument(
65
66
  "-s",
66
67
  "--scope",
@@ -295,7 +296,11 @@ def _split_add_args(argv: list[str]) -> tuple[list[str], list[str]]:
295
296
  return ["add", *option_args, *positional_args], extra_args
296
297
 
297
298
 
298
- def _build_add_server_config(args: argparse.Namespace) -> McpServerConfig:
299
+ def _build_add_server_config(
300
+ args: argparse.Namespace,
301
+ *,
302
+ command_name: str = "comate",
303
+ ) -> McpServerConfig:
299
304
  transport = str(args.transport or "stdio").strip().lower()
300
305
  header_entries = [str(item) for item in list(args.header or [])]
301
306
  env_entries = [str(item) for item in list(args.env or [])]
@@ -308,8 +313,8 @@ def _build_add_server_config(args: argparse.Namespace) -> McpServerConfig:
308
313
  or getattr(args, "callback_port", None)
309
314
  ):
310
315
  raise McpCliError(
311
- "OAuth flags are not supported by comate mcp add yet. "
312
- "Use headers for token-based HTTP/SSE MCP servers, or add a Comate-native oauth object via .mcp.json."
316
+ f"OAuth flags are not supported by {command_name} mcp add yet. "
317
+ "Use headers for token-based HTTP/SSE MCP servers, or add a native oauth object via .mcp.json."
313
318
  )
314
319
 
315
320
  if transport in {"http", "sse"}:
@@ -397,6 +402,21 @@ def _format_server_endpoint(cfg: McpServerConfig) -> str:
397
402
  return " ".join([command, *str_args]).strip()
398
403
 
399
404
 
405
+ def _format_stdio_cwd_line(
406
+ cfg: McpServerConfig,
407
+ *,
408
+ project_root: PathInput | None,
409
+ ) -> str | None:
410
+ server_type = str(cfg.get("type", "stdio")).strip().lower() # type: ignore[attr-defined]
411
+ if server_type != "stdio":
412
+ return None
413
+ try:
414
+ resolution = resolve_stdio_cwd(cfg, project_root=project_root, create=False)
415
+ except ValueError as exc:
416
+ return f"<invalid cwd: {exc}>"
417
+ return format_stdio_cwd_resolution(resolution)
418
+
419
+
400
420
  def _write_server_to_scope(
401
421
  *,
402
422
  name: str,
@@ -440,13 +460,18 @@ def _find_scopes_containing_server(
440
460
  return found
441
461
 
442
462
 
443
- def _cmd_add(args: argparse.Namespace, *, project_root: PathInput | None) -> None:
463
+ def _cmd_add(
464
+ args: argparse.Namespace,
465
+ *,
466
+ project_root: PathInput | None,
467
+ command_name: str = "comate",
468
+ ) -> None:
444
469
  scope = str(args.scope)
445
470
  name = str(args.name).strip()
446
471
  if not name:
447
472
  raise McpCliError("Server name cannot be empty")
448
473
 
449
- config = _build_add_server_config(args)
474
+ config = _build_add_server_config(args, command_name=command_name)
450
475
  path, existed = _write_server_to_scope(
451
476
  name=name,
452
477
  config=config,
@@ -461,11 +486,16 @@ def _cmd_add(args: argparse.Namespace, *, project_root: PathInput | None) -> Non
461
486
  )
462
487
 
463
488
 
464
- def _cmd_add_json(args: argparse.Namespace, *, project_root: PathInput | None) -> None:
489
+ def _cmd_add_json(
490
+ args: argparse.Namespace,
491
+ *,
492
+ project_root: PathInput | None,
493
+ command_name: str = "comate",
494
+ ) -> None:
465
495
  if _has_oauth_flags(args):
466
496
  raise McpCliError(
467
- "OAuth flags are not supported by comate mcp add-json yet. "
468
- "Use headers for token-based HTTP/SSE MCP servers, or add a Comate-native oauth object via .mcp.json."
497
+ f"OAuth flags are not supported by {command_name} mcp add-json yet. "
498
+ "Use headers for token-based HTTP/SSE MCP servers, or add a native oauth object via .mcp.json."
469
499
  )
470
500
 
471
501
  scope = str(args.scope)
@@ -496,7 +526,12 @@ def _cmd_add_json(args: argparse.Namespace, *, project_root: PathInput | None) -
496
526
  )
497
527
 
498
528
 
499
- def _cmd_remove(args: argparse.Namespace, *, project_root: PathInput | None) -> None:
529
+ def _cmd_remove(
530
+ args: argparse.Namespace,
531
+ *,
532
+ project_root: PathInput | None,
533
+ command_name: str = "comate",
534
+ ) -> None:
500
535
  name = str(args.name).strip()
501
536
  if not name:
502
537
  raise McpCliError("Server name cannot be empty")
@@ -509,7 +544,7 @@ def _cmd_remove(args: argparse.Namespace, *, project_root: PathInput | None) ->
509
544
  if len(found) > 1:
510
545
  scope_list = ", ".join(found)
511
546
  suggestions = "; ".join(
512
- f"comate mcp remove {name!r} --scope {scope}" for scope in found
547
+ f"{command_name} mcp remove {name!r} --scope {scope}" for scope in found
513
548
  )
514
549
  raise McpCliError(
515
550
  f"MCP server '{name}' exists in multiple scopes: {scope_list}. "
@@ -547,7 +582,9 @@ def _cmd_list(args: argparse.Namespace, *, project_root: PathInput | None) -> No
547
582
  return
548
583
 
549
584
  sys.stdout.write("Checking MCP server health...\n\n")
550
- health_rows = asyncio.run(collect_mcp_server_health(servers=servers))
585
+ health_rows = asyncio.run(
586
+ collect_mcp_server_health(servers=servers, project_root=project_root)
587
+ )
551
588
  health_by_alias = {row.alias: row for row in health_rows}
552
589
 
553
590
  for alias in sorted(servers.keys()):
@@ -565,9 +602,17 @@ def _cmd_list(args: argparse.Namespace, *, project_root: PathInput | None) -> No
565
602
  else f"{CROSS_MARK} Unknown"
566
603
  )
567
604
  sys.stdout.write(f"{alias}: {endpoint} ({server_type}) - {status}\n")
605
+ cwd_line = _format_stdio_cwd_line(cfg, project_root=project_root)
606
+ if cwd_line:
607
+ sys.stdout.write(f" cwd: {cwd_line}\n")
568
608
 
569
609
 
570
- def _cmd_get(args: argparse.Namespace, *, project_root: PathInput | None) -> None:
610
+ def _cmd_get(
611
+ args: argparse.Namespace,
612
+ *,
613
+ project_root: PathInput | None,
614
+ command_name: str = "comate",
615
+ ) -> None:
571
616
  name = str(args.name).strip()
572
617
  scope = str(args.scope)
573
618
  if not name:
@@ -594,7 +639,9 @@ def _cmd_get(args: argparse.Namespace, *, project_root: PathInput | None) -> Non
594
639
  scope_line = _scope_label(scope)
595
640
  remove_scope = scope
596
641
 
597
- health_rows = asyncio.run(collect_mcp_server_health(servers={name: cfg}))
642
+ health_rows = asyncio.run(
643
+ collect_mcp_server_health(servers={name: cfg}, project_root=project_root)
644
+ )
598
645
  status = f"{CROSS_MARK} Not connected"
599
646
  if health_rows:
600
647
  row = health_rows[0]
@@ -624,6 +671,9 @@ def _cmd_get(args: argparse.Namespace, *, project_root: PathInput | None) -> Non
624
671
  raw_args = cfg.get("args") or [] # type: ignore[attr-defined]
625
672
  if isinstance(raw_args, list) and raw_args:
626
673
  lines.append(f" Args: {' '.join(str(item) for item in raw_args)}")
674
+ cwd_line = _format_stdio_cwd_line(cfg, project_root=project_root)
675
+ if cwd_line:
676
+ lines.append(f" Cwd: {cwd_line}")
627
677
  env = cfg.get("env") or {} # type: ignore[attr-defined]
628
678
  if isinstance(env, dict) and env:
629
679
  lines.append(" Env:")
@@ -632,13 +682,18 @@ def _cmd_get(args: argparse.Namespace, *, project_root: PathInput | None) -> Non
632
682
 
633
683
  lines.append("")
634
684
  lines.append(
635
- f'To remove this server, run: comate mcp remove "{name}" --scope {remove_scope}'
685
+ f'To remove this server, run: {command_name} mcp remove "{name}" --scope {remove_scope}'
636
686
  )
637
687
  sys.stdout.write("\n".join(lines) + "\n")
638
688
 
639
689
 
640
- def run_mcp_command(argv: list[str], *, project_root: PathInput | None = None) -> None:
641
- parser = _build_parser()
690
+ def run_mcp_command(
691
+ argv: list[str],
692
+ *,
693
+ project_root: PathInput | None = None,
694
+ command_name: str = "comate",
695
+ ) -> None:
696
+ parser = _build_parser(command_name=command_name)
642
697
  if argv and argv[0] == "add":
643
698
  parser_argv, extra_args = _split_add_args(argv)
644
699
  parsed = parser.parse_args(parser_argv)
@@ -667,6 +722,9 @@ def run_mcp_command(argv: list[str], *, project_root: PathInput | None = None) -
667
722
  raise McpCliError(f"Unknown mcp command: {command}")
668
723
 
669
724
  try:
670
- handler(parsed, project_root=project_root)
725
+ if command in {"add", "add-json", "get", "remove"}:
726
+ handler(parsed, project_root=project_root, command_name=command_name)
727
+ else:
728
+ handler(parsed, project_root=project_root)
671
729
  except McpConfigError as exc:
672
730
  raise McpCliError(str(exc), exit_code=1) from exc