comate-cli 0.8.0__tar.gz → 0.8.2__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 (232) hide show
  1. comate_cli-0.8.2/CONTEXT.md +39 -0
  2. {comate_cli-0.8.0 → comate_cli-0.8.2}/PKG-INFO +1 -1
  3. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/app.py +82 -30
  4. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/config/model.py +5 -17
  5. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/config/picker.py +7 -6
  6. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/config/picker_state.py +7 -27
  7. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/config/store.py +19 -61
  8. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/custom_slash_commands.py +40 -2
  9. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/event_renderer.py +78 -0
  10. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/figures.py +8 -9
  11. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/history_printer.py +117 -7
  12. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/logging_adapter.py +20 -0
  13. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/logo.py +42 -7
  14. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/message_style.py +2 -2
  15. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/models.py +1 -0
  16. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/plugins/tabs/installed_tab.py +2 -1
  17. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/plugins/tabs/marketplaces_tab.py +2 -1
  18. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/question_view.py +2 -2
  19. comate_cli-0.8.2/comate_cli/terminal_agent/recap/__init__.py +5 -0
  20. comate_cli-0.8.2/comate_cli/terminal_agent/recap/controller.py +131 -0
  21. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/tips.py +1 -0
  22. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/tool_result_formatters.py +8 -6
  23. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/tool_result_store.py +2 -0
  24. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/transcript_viewer.py +5 -5
  25. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/tui.py +117 -31
  26. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/tui_parts/commands.py +27 -46
  27. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/tui_parts/input_behavior.py +12 -4
  28. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/tui_parts/key_bindings.py +10 -4
  29. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/tui_parts/render_panels.py +149 -8
  30. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/update_check.py +0 -58
  31. comate_cli-0.8.2/docs/cli-rpc-stdio-guide.md +395 -0
  32. {comate_cli-0.8.0 → comate_cli-0.8.2}/pyproject.toml +1 -1
  33. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/config/test_model.py +18 -37
  34. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/config/test_picker_state.py +14 -42
  35. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/config/test_picker_ui.py +12 -0
  36. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/config/test_roundtrip.py +3 -8
  37. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/config/test_store_load.py +43 -59
  38. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/config/test_store_save.py +41 -26
  39. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/conftest.py +2 -2
  40. comate_cli-0.8.2/tests/recap/test_idle_recap_controller.py +384 -0
  41. comate_cli-0.8.2/tests/recap/test_recap_history_rendering.py +100 -0
  42. comate_cli-0.8.2/tests/recap/test_tui_idle_recap_integration.py +230 -0
  43. comate_cli-0.8.2/tests/statusline/__init__.py +0 -0
  44. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_app_shutdown.py +71 -4
  45. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_app_startup_latency.py +1 -7
  46. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_app_token_cost_config.py +3 -7
  47. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_app_usage_line.py +0 -2
  48. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_completion_status_panel.py +82 -0
  49. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_custom_slash_commands.py +86 -0
  50. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_event_renderer_streaming.py +108 -5
  51. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_history_printer.py +51 -17
  52. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_history_printer_log.py +3 -3
  53. comate_cli-0.8.2/tests/test_history_printer_subtitle_position.py +368 -0
  54. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_history_printer_tool_fold.py +4 -4
  55. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_history_sync.py +17 -19
  56. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_installed_tab.py +1 -1
  57. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_interrupt_exit_semantics.py +3 -0
  58. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_logging_adapter.py +77 -0
  59. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_logo.py +13 -9
  60. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_marketplaces_tab.py +2 -2
  61. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_preflight.py +19 -8
  62. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_startup_profile.py +2 -2
  63. comate_cli-0.8.2/tests/test_tips.py +7 -0
  64. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_tool_result_formatters.py +63 -0
  65. comate_cli-0.8.2/tests/test_tui_builtin_slash_chain.py +213 -0
  66. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_tui_esc_queue.py +29 -2
  67. comate_cli-0.8.2/tests/test_tui_llm_retry_panel.py +184 -0
  68. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_tui_paste_newline_guard.py +9 -0
  69. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_tui_queue_preview.py +17 -0
  70. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_tui_queue_sdk_source.py +3 -0
  71. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_tui_team_messages.py +3 -0
  72. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_tui_thinking_display.py +3 -0
  73. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_update_check.py +0 -61
  74. comate_cli-0.8.2/tools/nico_preview.py +280 -0
  75. comate_cli-0.8.0/tests/test_history_printer_subtitle_position.py +0 -116
  76. {comate_cli-0.8.0 → comate_cli-0.8.2}/.gitignore +0 -0
  77. {comate_cli-0.8.0 → comate_cli-0.8.2}/CHANGELOG.md +0 -0
  78. {comate_cli-0.8.0 → comate_cli-0.8.2}/README.md +0 -0
  79. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/__init__.py +0 -0
  80. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/__main__.py +0 -0
  81. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/main.py +0 -0
  82. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/mcp_cli.py +0 -0
  83. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/__init__.py +0 -0
  84. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/animations.py +0 -0
  85. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/assistant_render.py +0 -0
  86. /comate_cli-0.8.0/comate_cli/terminal_agent/config/__init__.py → /comate_cli-0.8.2/comate_cli/terminal_agent/builtin_commands/.keep +0 -0
  87. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/codenames.py +0 -0
  88. {comate_cli-0.8.0/comate_cli/terminal_agent/plugins → comate_cli-0.8.2/comate_cli/terminal_agent/config}/__init__.py +0 -0
  89. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/env_utils.py +0 -0
  90. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/error_display.py +0 -0
  91. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/file_ref_hint.py +0 -0
  92. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/fragment_utils.py +0 -0
  93. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/goal_resume_view.py +0 -0
  94. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/input_geometry.py +0 -0
  95. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/layout_coordinator.py +0 -0
  96. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/markdown_render.py +0 -0
  97. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/mention_completer.py +0 -0
  98. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/path_context_hint.py +0 -0
  99. {comate_cli-0.8.0/comate_cli/terminal_agent/plugins/components → comate_cli-0.8.2/comate_cli/terminal_agent/plugins}/__init__.py +0 -0
  100. {comate_cli-0.8.0/comate_cli/terminal_agent/plugins/tabs → comate_cli-0.8.2/comate_cli/terminal_agent/plugins/components}/__init__.py +0 -0
  101. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/plugins/components/detail_view.py +0 -0
  102. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/plugins/components/plugin_list.py +0 -0
  103. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/plugins/components/search_box.py +0 -0
  104. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/plugins/components/tab_bar.py +0 -0
  105. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/plugins/marketplace_install_view.py +0 -0
  106. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/plugins/operation_state.py +0 -0
  107. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/plugins/plugin_picker.py +0 -0
  108. {comate_cli-0.8.0/comate_cli/terminal_agent/statusline → comate_cli-0.8.2/comate_cli/terminal_agent/plugins/tabs}/__init__.py +0 -0
  109. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/plugins/tabs/discover_tab.py +0 -0
  110. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/plugins/tabs/errors_tab.py +0 -0
  111. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/preflight.py +0 -0
  112. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/preflight_wizard.py +0 -0
  113. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/print_mode.py +0 -0
  114. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/resume_picker.py +0 -0
  115. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/resume_preview.py +0 -0
  116. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/resume_selector.py +0 -0
  117. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/rpc_protocol.py +0 -0
  118. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/rpc_stdio.py +0 -0
  119. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/selection_menu.py +0 -0
  120. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/slash_commands.py +0 -0
  121. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/startup.py +0 -0
  122. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/startup_profile.py +0 -0
  123. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/status_bar.py +0 -0
  124. {comate_cli-0.8.0/tests/config → comate_cli-0.8.2/comate_cli/terminal_agent/statusline}/__init__.py +0 -0
  125. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/statusline/model.py +0 -0
  126. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/statusline/picker.py +0 -0
  127. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/statusline/picker_state.py +0 -0
  128. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/statusline/store.py +0 -0
  129. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/text_effects.py +0 -0
  130. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/tool_fold.py +0 -0
  131. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/tool_result_viewer.py +0 -0
  132. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/tool_view.py +0 -0
  133. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/tui_parts/__init__.py +0 -0
  134. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/tui_parts/btw_view.py +0 -0
  135. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/tui_parts/history_sync.py +0 -0
  136. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/tui_parts/mcp_connecting_view.py +0 -0
  137. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/tui_parts/slash_command_registry.py +0 -0
  138. {comate_cli-0.8.0 → comate_cli-0.8.2}/comate_cli/terminal_agent/tui_parts/ui_mode.py +0 -0
  139. {comate_cli-0.8.0 → comate_cli-0.8.2}/dist-comate/.gitignore +0 -0
  140. {comate_cli-0.8.0 → comate_cli-0.8.2}/dist-comate/comate_cli-0.7.10-py3-none-any.whl +0 -0
  141. {comate_cli-0.8.0 → comate_cli-0.8.2}/dist-comate/comate_cli-0.7.10.tar.gz +0 -0
  142. {comate_cli-0.8.0 → comate_cli-0.8.2}/dist-hico/.gitignore +0 -0
  143. {comate_cli-0.8.0 → comate_cli-0.8.2}/dist-hico/hico_cli-0.7.17-py3-none-any.whl +0 -0
  144. {comate_cli-0.8.0 → comate_cli-0.8.2}/dist-hico/hico_cli-0.7.17.tar.gz +0 -0
  145. {comate_cli-0.8.0/tests/statusline → comate_cli-0.8.2/tests/config}/__init__.py +0 -0
  146. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/fixtures/fake_mcp_misbehaving_stdout.py +0 -0
  147. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/statusline/test_model.py +0 -0
  148. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/statusline/test_picker_state.py +0 -0
  149. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/statusline/test_store.py +0 -0
  150. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_animator_shuffle.py +0 -0
  151. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_app_mcp_preload.py +0 -0
  152. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_app_preflight_gate.py +0 -0
  153. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_app_print_mode.py +0 -0
  154. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_btw_slash_command.py +0 -0
  155. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_cli_project_root.py +0 -0
  156. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_compact_command_semantics.py +0 -0
  157. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_completion_context_activation.py +0 -0
  158. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_context_command.py +0 -0
  159. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_discover_tab.py +0 -0
  160. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_errors_tab.py +0 -0
  161. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_event_renderer.py +0 -0
  162. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_event_renderer_boundary.py +0 -0
  163. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_event_renderer_e2e.py +0 -0
  164. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_event_renderer_log_boundary.py +0 -0
  165. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_event_renderer_log_queue.py +0 -0
  166. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_event_renderer_tool_fold.py +0 -0
  167. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_file_ref_hint.py +0 -0
  168. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_format_error.py +0 -0
  169. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_goal_resume_tui.py +0 -0
  170. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_goal_resume_view.py +0 -0
  171. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_goal_slash_command.py +0 -0
  172. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_handle_error.py +0 -0
  173. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_history_sync_tool_fold.py +0 -0
  174. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_input_behavior.py +0 -0
  175. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_input_history.py +0 -0
  176. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_layout_coordinator.py +0 -0
  177. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_main_args.py +0 -0
  178. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_markdown_render.py +0 -0
  179. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_mcp_cli.py +0 -0
  180. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_mcp_slash_command.py +0 -0
  181. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_mention_completer.py +0 -0
  182. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_model_switch_command.py +0 -0
  183. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_path_context_hint.py +0 -0
  184. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_plugin_operation_state.py +0 -0
  185. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_plugin_slash_commands.py +0 -0
  186. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_plugin_tui_components.py +0 -0
  187. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_preflight_copilot.py +0 -0
  188. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_question_key_bindings.py +0 -0
  189. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_question_view.py +0 -0
  190. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_resume_picker.py +0 -0
  191. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_resume_preview.py +0 -0
  192. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_resume_selector.py +0 -0
  193. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_rewind_command_semantics.py +0 -0
  194. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_rpc_protocol.py +0 -0
  195. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_rpc_stdio_bridge.py +0 -0
  196. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_selection_menu.py +0 -0
  197. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_session_query_token_summary.py +0 -0
  198. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_shutdown_noise_guard.py +0 -0
  199. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_shutdown_noise_integration.py +0 -0
  200. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_skills_slash_command.py +0 -0
  201. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_slash_argument_hint.py +0 -0
  202. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_slash_clear.py +0 -0
  203. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_slash_completer.py +0 -0
  204. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_slash_registry.py +0 -0
  205. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_startup_import_budget.py +0 -0
  206. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_status_bar.py +0 -0
  207. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_status_bar_transient.py +0 -0
  208. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_task_panel_format.py +0 -0
  209. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_task_panel_key_bindings.py +0 -0
  210. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_task_panel_rendering.py +0 -0
  211. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_task_poll.py +0 -0
  212. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_tool_fold.py +0 -0
  213. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_tool_fold_panel.py +0 -0
  214. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_tool_result_store.py +0 -0
  215. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_tool_result_viewer.py +0 -0
  216. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_tool_result_viewer_key_bindings.py +0 -0
  217. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_tool_view.py +0 -0
  218. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_transcript_viewer.py +0 -0
  219. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_transcript_viewer_tool_fold.py +0 -0
  220. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_tui_elapsed_status.py +0 -0
  221. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_tui_local_interactive_barrier.py +0 -0
  222. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_tui_mcp_init_gate.py +0 -0
  223. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_tui_paste_placeholder.py +0 -0
  224. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_tui_scheduled_fire_log.py +0 -0
  225. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_tui_send_undo_window.py +0 -0
  226. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_tui_split_invariance.py +0 -0
  227. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_tui_startup_latency.py +0 -0
  228. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_tui_tool_result_registry_lifecycle.py +0 -0
  229. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_usage_command.py +0 -0
  230. {comate_cli-0.8.0 → comate_cli-0.8.2}/tests/test_wrap_user_text.py +0 -0
  231. {comate_cli-0.8.0 → comate_cli-0.8.2}/tools/logo_lab.py +0 -0
  232. {comate_cli-0.8.0 → comate_cli-0.8.2}/uv.lock +0 -0
@@ -0,0 +1,39 @@
1
+ # Comate CLI Slash Command Model
2
+
3
+ The comate_cli slash command system separates how a command is *executed* from *where its definition comes from*. Two orthogonal axes describe every registered command. The same word "builtin" previously appeared on both axes with different, conflicting override semantics — this glossary resolves that ambiguity.
4
+
5
+ ## Language
6
+
7
+ **Core Slash Command**:
8
+ A slash command backed by a hardcoded Python handler registered in `SLASH_COMMAND_SPECS` (e.g. `/help`, `/model`, `/exit`). Registered with `SlashCommandSource = "builtin"` on the *execution* axis.
9
+ _Avoid_: builtin command (ambiguous), native command
10
+
11
+ **Default Custom Command**:
12
+ A markdown-resource slash command shipped inside the `comate_cli` package under `terminal_agent/builtin_commands/`. Loaded via the custom-command pipeline (frontmatter + template), with `source_scope = "default"` on the *source* axis. User-authored custom commands of the same name override it.
13
+ _Avoid_: builtin command (ambiguous), bundled command
14
+
15
+ **Execution Axis** (`SlashCommandSource`):
16
+ Describes how a registered command is dispatched. Values: `"builtin"` (hardcoded handler), `"custom"` (markdown template render), `"skill"` (skill slash). Orthogonal to the Source Axis.
17
+
18
+ **Source Axis** (`CustomSlashCommand.source_scope`):
19
+ Describes where a markdown-template command's definition lives. Values: `"project"`, `"user"`, `"plugin"`, `"default"`. Determines override priority only.
20
+
21
+ **Reserved Name Set** (`builtin_names`):
22
+ The set of names derived from `SLASH_COMMAND_SPECS` (Core Slash Commands) that user-authored custom commands may not shadow. A custom command whose name collides with this set is silently skipped with a warning during discovery. **Default Custom Command names do NOT belong to this set** — they are overrideable by design.
23
+
24
+ **Override Priority Chain**:
25
+ `project > user > default` (plus `plugin` registered independently at runtime). When a Default Custom Command and a user/project custom command share a name, the higher-priority scope wins silently (no warning); only the winning entry reaches the registry.
26
+
27
+ ## Relationships
28
+
29
+ - A **Core Slash Command** is always registered with `source="builtin"` on the Execution Axis. Its name belongs to the **Reserved Name Set** and cannot be overridden by any markdown-template command.
30
+ - A **Default Custom Command** is registered with `source="custom"` on the Execution Axis (same dispatch path as project/user/plugin custom commands) and `source_scope="default"` on the Source Axis.
31
+ - The **Execution Axis** and **Source Axis** are independent. A single registered command carries a value on each.
32
+ - The word "builtin" appears only on the Execution Axis (`source="builtin"`) and refers exclusively to **Core Slash Commands**. The Source Axis value `"default"` refers exclusively to **Default Custom Commands**.
33
+ - **Default Custom Commands** are loaded from package resources via `importlib.resources`, parsed by the same frontmatter pipeline as project/user commands, and subject to the **Override Priority Chain** at registration time.
34
+ - A **Default Custom Command** parse failure is a developer-visible error (raised at discovery), not a user-visible warning, because package resources are trusted input maintained by the comate_cli maintainers.
35
+ - The **Reserved Name Set** gates discovery only; it prevents user-authored markdown commands from shadowing **Core Slash Commands**, and only Core Slash Commands.
36
+
37
+ ## Flagged ambiguities
38
+
39
+ - None currently. The original "builtin" collision between Execution Axis and Source Axis has been resolved by reserving "builtin" for Core Slash Commands and introducing "default" for the new shipped markdown resources.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: comate-cli
3
- Version: 0.8.0
3
+ Version: 0.8.2
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
@@ -54,7 +54,7 @@ async def _check_update(*, profiler: StartupProfiler | None = None) -> UpdateInf
54
54
  if profiler is not None:
55
55
  profiler.mark("impl.start")
56
56
  try:
57
- check_update, _, _, _ = _import_update_check_core()
57
+ check_update, _ = _import_update_check_core()
58
58
  return await check_update(log=logger, profiler=profiler)
59
59
  finally:
60
60
  if profiler is not None:
@@ -308,15 +308,11 @@ def _import_update_check_core():
308
308
  from comate_cli.terminal_agent.update_check import (
309
309
  check_update,
310
310
  get_pending_update_prompt_info,
311
- record_update_check_attempt,
312
- should_check_for_update,
313
311
  )
314
312
 
315
313
  return (
316
314
  check_update,
317
315
  get_pending_update_prompt_info,
318
- record_update_check_attempt,
319
- should_check_for_update,
320
316
  )
321
317
 
322
318
 
@@ -387,7 +383,7 @@ def _build_agent(
387
383
  profiler: StartupProfiler | None = None,
388
384
  extra_disallowed_tools: tuple[str, ...] | None = None,
389
385
  ) -> Agent:
390
- Agent, AgentConfig, CompactionConfig, EnvOptions = _import_sdk_agent_components(profiler)
386
+ Agent, AgentConfig, _, EnvOptions = _import_sdk_agent_components(profiler)
391
387
  from comate_cli.terminal_agent.config import store
392
388
 
393
389
  resolved_project_root = project_root or _resolve_cli_project_root()
@@ -407,9 +403,6 @@ def _build_agent(
407
403
  strict_hook_settings=False,
408
404
  memory_background_enabled=snapshot.memory_background_enabled,
409
405
  memory_dreaming_enabled=snapshot.memory_dreaming_enabled,
410
- compaction=CompactionConfig(
411
- threshold_ratio=snapshot.compaction_threshold_ratio,
412
- ),
413
406
  disallowed_tools=disallowed_tools,
414
407
  )
415
408
  if profiler is not None:
@@ -546,6 +539,69 @@ async def _run_print_mode(
546
539
  )
547
540
 
548
541
 
542
+ def _iter_exception_chain(exc: BaseException) -> Iterator[BaseException]:
543
+ seen: set[int] = set()
544
+ current: BaseException | None = exc
545
+ while current is not None and id(current) not in seen:
546
+ seen.add(id(current))
547
+ yield current
548
+ current = current.__cause__ or current.__context__
549
+
550
+
551
+ def _exception_traceback_mentions_memory_prefetch(exc: BaseException) -> bool:
552
+ for chained in _iter_exception_chain(exc):
553
+ tb = chained.__traceback__
554
+ while tb is not None:
555
+ code = tb.tb_frame.f_code
556
+ filename = code.co_filename
557
+ function_name = code.co_name
558
+ if function_name == "_prefetch_relevant_memories":
559
+ return True
560
+ if (
561
+ filename.endswith("memory_runtime.py")
562
+ and function_name == "_prefetch_relevant_memories"
563
+ ):
564
+ return True
565
+ tb = tb.tb_next
566
+ return False
567
+
568
+
569
+ def _loop_context_repr_mentions_memory_prefetch(context: dict) -> bool:
570
+ for key in ("future", "task", "handle"):
571
+ value = context.get(key)
572
+ if value is None:
573
+ continue
574
+ representation = repr(value)
575
+ if "_prefetch_relevant_memories" in representation:
576
+ return True
577
+ if "side_query:memory_select" in representation:
578
+ return True
579
+ return False
580
+
581
+
582
+ def _is_memory_prefetch_unretrieved_task_exception(context: dict) -> bool:
583
+ message = str(context.get("message") or "")
584
+ if "Task exception was never retrieved" not in message:
585
+ return False
586
+
587
+ exc = context.get("exception")
588
+ if not isinstance(exc, BaseException):
589
+ return False
590
+
591
+ try:
592
+ from comate_agent_sdk.llm.exceptions import ModelProviderError
593
+ except Exception:
594
+ return False
595
+
596
+ if not isinstance(exc, ModelProviderError):
597
+ return False
598
+
599
+ return (
600
+ _loop_context_repr_mentions_memory_prefetch(context)
601
+ or _exception_traceback_mentions_memory_prefetch(exc)
602
+ )
603
+
604
+
549
605
  def _install_event_loop_exception_handler() -> None:
550
606
  """Install a custom exception handler to suppress known MCP transport race conditions.
551
607
 
@@ -563,6 +619,17 @@ def _install_event_loop_exception_handler() -> None:
563
619
  if isinstance(exc, RuntimeError) and "cancel scope" in str(exc):
564
620
  logger.warning("MCP transport cleanup race detected (suppressed): %s", exc)
565
621
  return
622
+ if _is_memory_prefetch_unretrieved_task_exception(context):
623
+ logger.debug(
624
+ "Relevant memory prefetch task failure suppressed during shutdown: %s",
625
+ exc,
626
+ exc_info=(
627
+ (type(exc), exc, exc.__traceback__)
628
+ if isinstance(exc, BaseException)
629
+ else None
630
+ ),
631
+ )
632
+ return
566
633
  if _default is not None:
567
634
  _default(loop, context)
568
635
  else:
@@ -623,12 +690,7 @@ async def run(
623
690
  print_logo(console, project_root=project_root)
624
691
  profiler.mark("logo.printed")
625
692
 
626
- (
627
- _check_update_func,
628
- get_pending_update_prompt_info,
629
- _record_update_check_attempt,
630
- _should_check_for_update,
631
- ) = _import_update_check_core()
693
+ _check_update_func, get_pending_update_prompt_info = _import_update_check_core()
632
694
  pending_info = get_pending_update_prompt_info()
633
695
  if pending_info is not None:
634
696
  should_stop = await _handle_update_on_launch(pending_info)
@@ -719,21 +781,11 @@ async def run(
719
781
  profiler.mark("tui.init.done")
720
782
  tui.add_resume_history(mode)
721
783
  update_check_task: asyncio.Task[None] | None = None
722
- (
723
- _check_update_func,
724
- _get_pending_update_prompt_info,
725
- record_update_check_attempt,
726
- should_check_for_update,
727
- ) = _import_update_check_core()
728
- if should_check_for_update():
729
- # attempt 语义:先记录尝试时间,确保 interval 内最多发起一次网络检查
730
- record_update_check_attempt()
731
- update_check_task = _schedule_update_check_on_launch(
732
- renderer=renderer,
733
- profiler=profiler,
734
- )
735
- else:
736
- profiler.mark("update_check.throttled")
784
+ _check_update_func, _get_pending_update_prompt_info = _import_update_check_core()
785
+ update_check_task = _schedule_update_check_on_launch(
786
+ renderer=renderer,
787
+ profiler=profiler,
788
+ )
737
789
 
738
790
  async def _mcp_loader() -> None:
739
791
  await _preload_mcp_in_tui(session, profiler=profiler.child("mcp"))
@@ -1,6 +1,6 @@
1
1
  """ConfigSnapshot:/config UI、store、startup 三处共享的偏好快照。
2
2
 
3
- Revision 3: 6 项偏好(display_thinking, model_level, compaction, memory_background, memory_dreaming, token_cost)。
3
+ Revision 5: 6 项偏好(display_thinking, model_level, memory_background, memory_dreaming, token_cost, recap)。
4
4
  """
5
5
  from __future__ import annotations
6
6
 
@@ -8,13 +8,6 @@ from dataclasses import dataclass, replace
8
8
  from typing import Literal
9
9
 
10
10
  ModelLevelPreset = Literal["LOW", "MID", "HIGH"]
11
- ThresholdPreset = Literal["conservative", "balanced", "aggressive"]
12
-
13
- COMPACT_PRESETS: dict[str, float] = {
14
- "conservative": 0.65,
15
- "balanced": 0.80,
16
- "aggressive": 0.92,
17
- }
18
11
 
19
12
 
20
13
  @dataclass(frozen=True, slots=True)
@@ -23,11 +16,10 @@ class ConfigSnapshot:
23
16
 
24
17
  display_thinking: bool = False
25
18
  model_level: ModelLevelPreset = "MID"
26
- compaction_threshold_ratio: float = 0.80
27
- compaction_threshold_preset: ThresholdPreset = "balanced"
28
19
  memory_background_enabled: bool = True
29
20
  memory_dreaming_enabled: bool = True
30
21
  token_cost_enabled: bool = False
22
+ recap_enabled: bool = True
31
23
 
32
24
  @classmethod
33
25
  def default(cls) -> "ConfigSnapshot":
@@ -39,13 +31,6 @@ class ConfigSnapshot:
39
31
  def with_model_level(self, level: ModelLevelPreset) -> "ConfigSnapshot":
40
32
  return replace(self, model_level=level)
41
33
 
42
- def with_threshold_preset(self, preset: ThresholdPreset) -> "ConfigSnapshot":
43
- return replace(
44
- self,
45
- compaction_threshold_preset=preset,
46
- compaction_threshold_ratio=COMPACT_PRESETS[preset],
47
- )
48
-
49
34
  def with_memory_background(self, enabled: bool) -> "ConfigSnapshot":
50
35
  return replace(self, memory_background_enabled=enabled)
51
36
 
@@ -54,3 +39,6 @@ class ConfigSnapshot:
54
39
 
55
40
  def with_token_cost(self, enabled: bool) -> "ConfigSnapshot":
56
41
  return replace(self, token_cost_enabled=enabled)
42
+
43
+ def with_recap(self, enabled: bool) -> "ConfigSnapshot":
44
+ return replace(self, recap_enabled=enabled)
@@ -3,7 +3,7 @@
3
3
  仿 plugins/plugin_picker.py:PluginPickerUI 的模式:
4
4
  enter() → container → focus_target() → handle_*。
5
5
 
6
- Revision 3: 6 行 ConfigField。
6
+ Revision 5: 6 行 ConfigField。
7
7
  """
8
8
  from __future__ import annotations
9
9
 
@@ -34,27 +34,28 @@ logger = logging.getLogger(__name__)
34
34
  _FIELD_LABELS: dict[ConfigField, str] = {
35
35
  ConfigField.DISPLAY_THINKING: "Thinking display",
36
36
  ConfigField.MODEL_LEVEL: "Default model level",
37
- ConfigField.AUTO_COMPACT_THRESHOLD: "Auto compact threshold",
38
37
  ConfigField.MEMORY_BACKGROUND: "Memory background",
39
38
  ConfigField.MEMORY_DREAMING: "Memory dreaming",
40
39
  ConfigField.TOKEN_COST: "Token cost",
40
+ ConfigField.RECAP: "Recap",
41
41
  }
42
42
 
43
43
  _FIELD_ORDER = (
44
44
  ConfigField.DISPLAY_THINKING,
45
45
  ConfigField.MODEL_LEVEL,
46
- ConfigField.AUTO_COMPACT_THRESHOLD,
47
46
  ConfigField.MEMORY_BACKGROUND,
48
47
  ConfigField.MEMORY_DREAMING,
49
48
  ConfigField.TOKEN_COST,
49
+ ConfigField.RECAP,
50
50
  )
51
51
 
52
- _ENUM_FIELDS = {ConfigField.MODEL_LEVEL, ConfigField.AUTO_COMPACT_THRESHOLD}
52
+ _ENUM_FIELDS = {ConfigField.MODEL_LEVEL}
53
53
  _BOOL_FIELDS = {
54
54
  ConfigField.DISPLAY_THINKING,
55
55
  ConfigField.MEMORY_BACKGROUND,
56
56
  ConfigField.MEMORY_DREAMING,
57
57
  ConfigField.TOKEN_COST,
58
+ ConfigField.RECAP,
58
59
  }
59
60
 
60
61
 
@@ -160,14 +161,14 @@ class ConfigPickerUI:
160
161
  return f"[ {'on ' if snap.display_thinking else 'off'} ]"
161
162
  if f == ConfigField.MODEL_LEVEL:
162
163
  return f"[ {snap.model_level} ]"
163
- if f == ConfigField.AUTO_COMPACT_THRESHOLD:
164
- return f"[ {snap.compaction_threshold_preset} ({snap.compaction_threshold_ratio:.2f}) ]"
165
164
  if f == ConfigField.MEMORY_BACKGROUND:
166
165
  return f"[ {'on ' if snap.memory_background_enabled else 'off'} ]"
167
166
  if f == ConfigField.MEMORY_DREAMING:
168
167
  return f"[ {'on ' if snap.memory_dreaming_enabled else 'off'} ]"
169
168
  if f == ConfigField.TOKEN_COST:
170
169
  return f"[ {'on ' if snap.token_cost_enabled else 'off'} ]"
170
+ if f == ConfigField.RECAP:
171
+ return f"[ {'on ' if snap.recap_enabled else 'off'} ]"
171
172
  return ""
172
173
 
173
174
  def _subview_fragments(self) -> list[tuple[str, str]]:
@@ -1,6 +1,6 @@
1
1
  """ConfigPicker 的纯逻辑状态机——不依赖 prompt_toolkit。
2
2
 
3
- Revision 3: 6 行 ConfigField。
3
+ Revision 5: 6 行 ConfigField。
4
4
  """
5
5
  from __future__ import annotations
6
6
 
@@ -9,20 +9,18 @@ from enum import Enum
9
9
  from typing import Callable
10
10
 
11
11
  from .model import (
12
- COMPACT_PRESETS,
13
12
  ConfigSnapshot,
14
13
  ModelLevelPreset,
15
- ThresholdPreset,
16
14
  )
17
15
 
18
16
 
19
17
  class ConfigField(Enum):
20
18
  DISPLAY_THINKING = "display_thinking"
21
19
  MODEL_LEVEL = "model_level"
22
- AUTO_COMPACT_THRESHOLD = "auto_compact_threshold"
23
20
  MEMORY_BACKGROUND = "memory_background"
24
21
  MEMORY_DREAMING = "memory_dreaming"
25
22
  TOKEN_COST = "token_cost"
23
+ RECAP = "recap"
26
24
 
27
25
 
28
26
  class ViewMode(Enum):
@@ -51,19 +49,12 @@ class PickerExit:
51
49
  _FIELD_ORDER = (
52
50
  ConfigField.DISPLAY_THINKING,
53
51
  ConfigField.MODEL_LEVEL,
54
- ConfigField.AUTO_COMPACT_THRESHOLD,
55
52
  ConfigField.MEMORY_BACKGROUND,
56
53
  ConfigField.MEMORY_DREAMING,
57
54
  ConfigField.TOKEN_COST,
55
+ ConfigField.RECAP,
58
56
  )
59
57
 
60
- _THRESHOLD_VALUES: tuple[ThresholdPreset, ...] = ("conservative", "balanced", "aggressive")
61
- _THRESHOLD_DESCRIPTIONS: dict[str, str] = {
62
- "conservative": "0.65 — compact more often; smaller per-call ctx.",
63
- "balanced": "0.80 — default. Compact when ctx >= 80% utilized.",
64
- "aggressive": "0.92 — compact late; larger ctx per call.",
65
- }
66
-
67
58
  _MODEL_LEVEL_VALUES: tuple[ModelLevelPreset, ...] = ("LOW", "MID", "HIGH")
68
59
  _MODEL_LEVEL_DESCRIPTIONS: dict[str, str] = {
69
60
  "LOW": "Fastest, cheapest. Good for simple tasks.",
@@ -98,11 +89,6 @@ class ConfigPickerState:
98
89
  def subview_options(self) -> list[SubviewOption]:
99
90
  if self.view_mode != ViewMode.SUBVIEW or self.subview_field is None:
100
91
  return []
101
- if self.subview_field == ConfigField.AUTO_COMPACT_THRESHOLD:
102
- return [
103
- SubviewOption(v, v.capitalize(), _THRESHOLD_DESCRIPTIONS[v])
104
- for v in _THRESHOLD_VALUES
105
- ]
106
92
  if self.subview_field == ConfigField.MODEL_LEVEL:
107
93
  return [
108
94
  SubviewOption(v, v, _MODEL_LEVEL_DESCRIPTIONS[v])
@@ -140,10 +126,8 @@ class ConfigPickerState:
140
126
  self.draft = self.draft.with_memory_dreaming(not self.draft.memory_dreaming_enabled)
141
127
  elif f == ConfigField.TOKEN_COST:
142
128
  self.draft = self.draft.with_token_cost(not self.draft.token_cost_enabled)
143
- elif f == ConfigField.AUTO_COMPACT_THRESHOLD:
144
- cur = _THRESHOLD_VALUES.index(self.draft.compaction_threshold_preset)
145
- next_v = _THRESHOLD_VALUES[(cur + 1) % len(_THRESHOLD_VALUES)]
146
- self.draft = self.draft.with_threshold_preset(next_v)
129
+ elif f == ConfigField.RECAP:
130
+ self.draft = self.draft.with_recap(not self.draft.recap_enabled)
147
131
  elif f == ConfigField.MODEL_LEVEL:
148
132
  cur = _MODEL_LEVEL_VALUES.index(self.draft.model_level)
149
133
  next_v = _MODEL_LEVEL_VALUES[(cur + 1) % len(_MODEL_LEVEL_VALUES)]
@@ -152,7 +136,7 @@ class ConfigPickerState:
152
136
  def handle_enter(self) -> PickerExit | None:
153
137
  if self.view_mode == ViewMode.MAIN:
154
138
  f = self.focused_field
155
- if f in (ConfigField.AUTO_COMPACT_THRESHOLD, ConfigField.MODEL_LEVEL):
139
+ if f == ConfigField.MODEL_LEVEL:
156
140
  self.view_mode = ViewMode.SUBVIEW
157
141
  self.subview_field = f
158
142
  self.subview_cursor = self._initial_subview_cursor(f)
@@ -166,9 +150,7 @@ class ConfigPickerState:
166
150
  self._return_to_main()
167
151
  return None
168
152
  chosen = opts[self.subview_cursor].value
169
- if self.subview_field == ConfigField.AUTO_COMPACT_THRESHOLD:
170
- self.draft = self.draft.with_threshold_preset(chosen) # type: ignore[arg-type]
171
- elif self.subview_field == ConfigField.MODEL_LEVEL:
153
+ if self.subview_field == ConfigField.MODEL_LEVEL:
172
154
  self.draft = self.draft.with_model_level(chosen) # type: ignore[arg-type]
173
155
  self._return_to_main()
174
156
  return None
@@ -202,8 +184,6 @@ class ConfigPickerState:
202
184
 
203
185
  # ---- helpers --------------------------------------------------------
204
186
  def _initial_subview_cursor(self, f: ConfigField) -> int:
205
- if f == ConfigField.AUTO_COMPACT_THRESHOLD:
206
- return _THRESHOLD_VALUES.index(self.draft.compaction_threshold_preset)
207
187
  if f == ConfigField.MODEL_LEVEL:
208
188
  return _MODEL_LEVEL_VALUES.index(self.draft.model_level)
209
189
  return 0
@@ -16,15 +16,12 @@ from typing import Any, get_args
16
16
  from comate_agent_sdk.agent.settings import USER_SETTINGS_PATH, _write_text_atomic
17
17
 
18
18
  from .model import (
19
- COMPACT_PRESETS,
20
19
  ConfigSnapshot,
21
20
  ModelLevelPreset,
22
- ThresholdPreset,
23
21
  )
24
22
 
25
23
  logger = logging.getLogger(__name__)
26
24
 
27
- _VALID_PRESETS = set(get_args(ThresholdPreset))
28
25
  _VALID_MODEL_LEVELS = {v.lower() for v in get_args(ModelLevelPreset)}
29
26
  _TRUTHY = {"true", "1", "on", "yes"}
30
27
  _FALSY = {"false", "0", "off", "no"}
@@ -32,9 +29,9 @@ _CLI_SECTION = "comate_cli"
32
29
  _CLI_CONFIG_SECTION = "config"
33
30
  _CLI_CONFIG_DISPLAY_SECTION = "display"
34
31
  _CLI_CONFIG_MODEL_SECTION = "model"
35
- _CLI_CONFIG_COMPACTION_SECTION = "compaction"
36
32
  _CLI_CONFIG_MEMORY_SECTION = "memory"
37
33
  _CLI_CONFIG_TOKEN_COST_SECTION = "token_cost"
34
+ _CLI_CONFIG_RECAP_SECTION = "recap"
38
35
 
39
36
 
40
37
  def _settings_path() -> Path:
@@ -143,19 +140,6 @@ def _apply_legacy_settings(snapshot: ConfigSnapshot, data: Mapping[str, Any]) ->
143
140
  model,
144
141
  )
145
142
 
146
- # compaction.auto_threshold_preset
147
- compaction = data.get("compaction") or {}
148
- if isinstance(compaction, dict):
149
- preset = compaction.get("auto_threshold_preset")
150
- if isinstance(preset, str):
151
- if preset in _VALID_PRESETS:
152
- snapshot = snapshot.with_threshold_preset(preset) # type: ignore[arg-type]
153
- else:
154
- logger.warning(
155
- "settings.json compaction.auto_threshold_preset 非法 (%r),回退 balanced",
156
- preset,
157
- )
158
-
159
143
  # memory_state.enabled
160
144
  memory_state = data.get("memory_state") or {}
161
145
  if isinstance(memory_state, dict) and isinstance(memory_state.get("enabled"), bool):
@@ -211,40 +195,6 @@ def _apply_cli_config(snapshot: ConfigSnapshot, section: dict[str, Any]) -> Conf
211
195
  raw_model,
212
196
  )
213
197
 
214
- compaction = _named_object(
215
- section,
216
- _CLI_CONFIG_COMPACTION_SECTION,
217
- "comate_cli.config.compaction",
218
- )
219
- raw_preset = _named_string(
220
- compaction,
221
- "threshold_preset",
222
- "comate_cli.config.compaction.threshold_preset",
223
- )
224
- if raw_preset is not None:
225
- if raw_preset in _VALID_PRESETS:
226
- snapshot = snapshot.with_threshold_preset(raw_preset) # type: ignore[arg-type]
227
- raw_ratio = compaction.get("threshold_ratio")
228
- if isinstance(raw_ratio, (int, float)) and not isinstance(raw_ratio, bool):
229
- expected = COMPACT_PRESETS[raw_preset]
230
- if float(raw_ratio) != expected:
231
- logger.warning(
232
- "settings.json comate_cli.config.compaction.threshold_ratio "
233
- "与 threshold_preset 不一致 (%r != %r),以 preset 为准",
234
- raw_ratio,
235
- expected,
236
- )
237
- else:
238
- logger.warning(
239
- "settings.json comate_cli.config.compaction.threshold_preset 非法 (%r),保留已有值",
240
- raw_preset,
241
- )
242
- elif "threshold_ratio" in compaction:
243
- logger.warning(
244
- "settings.json comate_cli.config.compaction.threshold_ratio 缺少 "
245
- "threshold_preset,保留已有 preset 派生值"
246
- )
247
-
248
198
  memory = _named_object(
249
199
  section,
250
200
  _CLI_CONFIG_MEMORY_SECTION,
@@ -279,6 +229,19 @@ def _apply_cli_config(snapshot: ConfigSnapshot, section: dict[str, Any]) -> Conf
279
229
  if parsed_bool is not None:
280
230
  snapshot = snapshot.with_token_cost(parsed_bool)
281
231
 
232
+ recap = _named_object(
233
+ section,
234
+ _CLI_CONFIG_RECAP_SECTION,
235
+ "comate_cli.config.recap",
236
+ )
237
+ parsed_bool = _named_bool(
238
+ recap,
239
+ "enabled",
240
+ "comate_cli.config.recap.enabled",
241
+ )
242
+ if parsed_bool is not None:
243
+ snapshot = snapshot.with_recap(parsed_bool)
244
+
282
245
  return snapshot
283
246
 
284
247
 
@@ -310,10 +273,6 @@ def save(snapshot: ConfigSnapshot) -> SaveResult:
310
273
  def build_cli_config_settings(snapshot: ConfigSnapshot) -> dict[str, Any]:
311
274
  """Build the canonical comate_cli.config settings subtree."""
312
275
  return {
313
- "compaction": {
314
- "threshold_preset": snapshot.compaction_threshold_preset,
315
- "threshold_ratio": snapshot.compaction_threshold_ratio,
316
- },
317
276
  "display": {"thinking": snapshot.display_thinking},
318
277
  "memory": {
319
278
  "background_enabled": snapshot.memory_background_enabled,
@@ -321,6 +280,7 @@ def build_cli_config_settings(snapshot: ConfigSnapshot) -> dict[str, Any]:
321
280
  },
322
281
  "model": {"level": snapshot.model_level.lower()},
323
282
  "token_cost": {"enabled": snapshot.token_cost_enabled},
283
+ "recap": {"enabled": snapshot.recap_enabled},
324
284
  }
325
285
 
326
286
 
@@ -345,7 +305,10 @@ def apply_cli_config_snapshot_to_settings(
345
305
  """Apply canonical comate_cli.config plus SDK mirrors to a settings object."""
346
306
  data = deepcopy(dict(existing))
347
307
  cli = _ensure_object(data, _CLI_SECTION)
348
- cli[_CLI_CONFIG_SECTION] = build_cli_config_settings(snapshot)
308
+ existing_cli_config = cli.get(_CLI_CONFIG_SECTION)
309
+ cli_config = deepcopy(existing_cli_config) if isinstance(existing_cli_config, dict) else {}
310
+ cli_config.update(build_cli_config_settings(snapshot))
311
+ cli[_CLI_CONFIG_SECTION] = cli_config
349
312
 
350
313
  data["model"] = snapshot.model_level.lower()
351
314
 
@@ -362,11 +325,6 @@ def apply_cli_config_snapshot_to_settings(
362
325
  display.pop("thinking", None)
363
326
  _cleanup_empty_object(data, "display")
364
327
 
365
- compaction = data.get("compaction")
366
- if isinstance(compaction, dict):
367
- compaction.pop("auto_threshold_preset", None)
368
- _cleanup_empty_object(data, "compaction")
369
-
370
328
  return data
371
329
 
372
330
 
@@ -5,6 +5,7 @@ import logging
5
5
  import re
6
6
  import shlex
7
7
  from dataclasses import dataclass
8
+ from importlib.resources import as_file, files
8
9
  from pathlib import Path
9
10
  from typing import Literal
10
11
 
@@ -25,6 +26,9 @@ FILE_REF_PATTERN = re.compile(r"(?<!\S)@([^\s]+)")
25
26
  _MARKER_PREFIX = "<<__COMATE_CUSTOM_BLOCK_"
26
27
  _MARKER_SUFFIX = "__>>"
27
28
 
29
+ _BUILTIN_COMMANDS_PACKAGE = "comate_cli.terminal_agent"
30
+ _BUILTIN_COMMANDS_RESOURCE = "builtin_commands"
31
+
28
32
 
29
33
  class CustomSlashExpandError(RuntimeError):
30
34
  pass
@@ -35,7 +39,7 @@ class CustomSlashCommand:
35
39
  name: str
36
40
  description: str
37
41
  template: str
38
- source_scope: Literal["project", "user", "plugin"]
42
+ source_scope: Literal["project", "user", "plugin", "default"]
39
43
  namespace: str
40
44
  source_path: Path
41
45
  argument_hint: str | None = None
@@ -114,10 +118,44 @@ def discover_custom_slash_commands(
114
118
  return CustomSlashLoadResult(commands=tuple(commands), warnings=tuple(warnings))
115
119
 
116
120
 
121
+ def discover_default_slash_commands() -> CustomSlashLoadResult:
122
+ """加载随包分发的 default scope custom commands。
123
+
124
+ default scope 的 md 资源由 comate_cli 维护者维护,是可信输入。解析失败
125
+ 直接 raise CustomSlashExpandError(developer-visible error),不返回
126
+ warnings;这与 project/user scope 把 warnings 塞给终端用户的语义不同。
127
+
128
+ 扁平布局:扫描 ``comate_cli.terminal_agent.builtin_commands`` 下的所有
129
+ ``*.md`` 资源,namespace 强制为空字符串。非 .md 文件被忽略。
130
+ """
131
+ root = files(_BUILTIN_COMMANDS_PACKAGE).joinpath(_BUILTIN_COMMANDS_RESOURCE)
132
+ if not root.is_dir():
133
+ return CustomSlashLoadResult(commands=(), warnings=())
134
+
135
+ commands: list[CustomSlashCommand] = []
136
+ for entry in sorted(root.iterdir(), key=lambda item: item.name):
137
+ if not entry.name.endswith(".md"):
138
+ continue
139
+ with as_file(entry) as path:
140
+ parsed = _parse_custom_command_file(
141
+ file_path=path,
142
+ scope="default",
143
+ root_dir=path.parent,
144
+ )
145
+ if isinstance(parsed, str):
146
+ raise CustomSlashExpandError(
147
+ f"Built-in command resource {entry.name} is malformed: {parsed}"
148
+ )
149
+ commands.append(parsed)
150
+
151
+ commands.sort(key=lambda item: item.name.lower())
152
+ return CustomSlashLoadResult(commands=tuple(commands), warnings=())
153
+
154
+
117
155
  def _parse_custom_command_file(
118
156
  *,
119
157
  file_path: Path,
120
- scope: Literal["project", "user"],
158
+ scope: Literal["project", "user", "default"],
121
159
  root_dir: Path,
122
160
  ) -> CustomSlashCommand | str:
123
161
  command_name = file_path.stem.strip()