comate-cli 0.6.2__tar.gz → 0.7.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 (201) hide show
  1. {comate_cli-0.6.2 → comate_cli-0.7.0}/.gitignore +6 -0
  2. comate_cli-0.7.0/AGENTS.md +115 -0
  3. comate_cli-0.7.0/CHANGELOG.md +50 -0
  4. {comate_cli-0.6.2 → comate_cli-0.7.0}/PKG-INFO +27 -2
  5. comate_cli-0.7.0/README.md +41 -0
  6. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/main.py +100 -2
  7. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/animations.py +27 -5
  8. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/app.py +17 -6
  9. comate_cli-0.7.0/comate_cli/terminal_agent/config/model.py +52 -0
  10. comate_cli-0.7.0/comate_cli/terminal_agent/config/picker.py +232 -0
  11. comate_cli-0.7.0/comate_cli/terminal_agent/config/picker_state.py +210 -0
  12. comate_cli-0.7.0/comate_cli/terminal_agent/config/store.py +173 -0
  13. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/error_display.py +25 -7
  14. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/event_renderer.py +266 -328
  15. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/history_printer.py +99 -41
  16. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/logging_adapter.py +84 -5
  17. comate_cli-0.7.0/comate_cli/terminal_agent/markdown_render.py +138 -0
  18. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/mention_completer.py +82 -13
  19. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/models.py +5 -0
  20. comate_cli-0.7.0/comate_cli/terminal_agent/plugins/tabs/__init__.py +0 -0
  21. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/plugins/tabs/errors_tab.py +2 -11
  22. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/preflight.py +10 -333
  23. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/question_view.py +24 -0
  24. comate_cli-0.7.0/comate_cli/terminal_agent/resume_picker.py +613 -0
  25. comate_cli-0.7.0/comate_cli/terminal_agent/resume_preview.py +140 -0
  26. comate_cli-0.7.0/comate_cli/terminal_agent/resume_selector.py +87 -0
  27. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/slash_commands.py +16 -0
  28. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/tips.py +11 -0
  29. comate_cli-0.7.0/comate_cli/terminal_agent/tool_fold.py +206 -0
  30. comate_cli-0.7.0/comate_cli/terminal_agent/tool_result_formatters.py +923 -0
  31. comate_cli-0.7.0/comate_cli/terminal_agent/tool_result_store.py +75 -0
  32. comate_cli-0.7.0/comate_cli/terminal_agent/tool_result_viewer.py +214 -0
  33. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/tool_view.py +71 -6
  34. comate_cli-0.7.0/comate_cli/terminal_agent/transcript_viewer.py +404 -0
  35. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/tui.py +254 -122
  36. comate_cli-0.7.0/comate_cli/terminal_agent/tui_parts/btw_view.py +426 -0
  37. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/tui_parts/commands.py +387 -204
  38. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/tui_parts/history_sync.py +49 -4
  39. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/tui_parts/input_behavior.py +105 -9
  40. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/tui_parts/key_bindings.py +138 -33
  41. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/tui_parts/mcp_connecting_view.py +10 -2
  42. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/tui_parts/render_panels.py +104 -110
  43. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/tui_parts/ui_mode.py +2 -0
  44. comate_cli-0.7.0/docs/hooks.md +1047 -0
  45. comate_cli-0.7.0/docs/superpowers/plans/2026-05-22-log-style-implementation-plan.md +444 -0
  46. comate_cli-0.7.0/docs/superpowers/specs/2026-05-22-comate-cli-log-styling-design.md +71 -0
  47. comate_cli-0.7.0/docs/superpowers/specs/2026-05-22-log-style-optimization-design.md +114 -0
  48. {comate_cli-0.6.2 → comate_cli-0.7.0}/pyproject.toml +7 -2
  49. comate_cli-0.7.0/report.md +382 -0
  50. comate_cli-0.7.0/tests/config/__init__.py +0 -0
  51. comate_cli-0.7.0/tests/config/test_model.py +88 -0
  52. comate_cli-0.7.0/tests/config/test_picker_state.py +205 -0
  53. comate_cli-0.7.0/tests/config/test_picker_ui.py +42 -0
  54. comate_cli-0.7.0/tests/config/test_roundtrip.py +75 -0
  55. comate_cli-0.7.0/tests/config/test_store_load.py +112 -0
  56. comate_cli-0.7.0/tests/config/test_store_save.py +103 -0
  57. comate_cli-0.7.0/tests/fixtures/fake_mcp_misbehaving_stdout.py +75 -0
  58. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_app_shutdown.py +66 -1
  59. comate_cli-0.7.0/tests/test_btw_slash_command.py +554 -0
  60. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_compact_command_semantics.py +4 -4
  61. comate_cli-0.7.0/tests/test_completion_context_activation.py +119 -0
  62. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_completion_status_panel.py +105 -0
  63. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_discover_tab.py +27 -20
  64. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_event_renderer.py +559 -41
  65. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_event_renderer_log_boundary.py +21 -13
  66. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_event_renderer_log_queue.py +37 -12
  67. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_event_renderer_streaming.py +30 -1
  68. comate_cli-0.7.0/tests/test_event_renderer_tool_fold.py +146 -0
  69. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_format_error.py +52 -0
  70. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_handle_error.py +10 -0
  71. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_history_printer.py +69 -1
  72. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_history_printer_log.py +54 -30
  73. comate_cli-0.7.0/tests/test_history_printer_subtitle_position.py +78 -0
  74. comate_cli-0.7.0/tests/test_history_printer_tool_fold.py +86 -0
  75. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_history_sync.py +146 -6
  76. comate_cli-0.7.0/tests/test_history_sync_tool_fold.py +98 -0
  77. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_installed_tab.py +11 -7
  78. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_interrupt_exit_semantics.py +81 -4
  79. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_logging_adapter.py +279 -1
  80. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_main_args.py +16 -0
  81. comate_cli-0.7.0/tests/test_markdown_render.py +102 -0
  82. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_mcp_cli.py +2 -2
  83. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_mcp_slash_command.py +229 -1
  84. comate_cli-0.7.0/tests/test_mention_completer.py +765 -0
  85. comate_cli-0.7.0/tests/test_plugin_slash_commands.py +107 -0
  86. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_plugin_tui_components.py +5 -11
  87. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_preflight.py +17 -21
  88. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_preflight_copilot.py +2 -2
  89. comate_cli-0.7.0/tests/test_resume_picker.py +429 -0
  90. comate_cli-0.7.0/tests/test_resume_preview.py +134 -0
  91. comate_cli-0.7.0/tests/test_resume_selector.py +109 -0
  92. comate_cli-0.7.0/tests/test_rewind_command_semantics.py +203 -0
  93. comate_cli-0.7.0/tests/test_session_query_token_summary.py +127 -0
  94. comate_cli-0.7.0/tests/test_shutdown_noise_guard.py +185 -0
  95. comate_cli-0.7.0/tests/test_shutdown_noise_integration.py +116 -0
  96. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_skills_slash_command.py +3 -27
  97. comate_cli-0.7.0/tests/test_slash_clear.py +350 -0
  98. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_task_panel_key_bindings.py +1 -3
  99. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_task_panel_rendering.py +3 -3
  100. comate_cli-0.7.0/tests/test_tool_fold.py +89 -0
  101. comate_cli-0.7.0/tests/test_tool_fold_panel.py +159 -0
  102. comate_cli-0.7.0/tests/test_tool_result_formatters.py +733 -0
  103. comate_cli-0.7.0/tests/test_tool_result_store.py +145 -0
  104. comate_cli-0.7.0/tests/test_tool_result_viewer.py +135 -0
  105. comate_cli-0.7.0/tests/test_tool_result_viewer_key_bindings.py +97 -0
  106. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_tool_view.py +35 -1
  107. comate_cli-0.7.0/tests/test_transcript_viewer.py +249 -0
  108. comate_cli-0.7.0/tests/test_transcript_viewer_tool_fold.py +47 -0
  109. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_tui_esc_queue.py +0 -3
  110. comate_cli-0.7.0/tests/test_tui_paste_newline_guard.py +260 -0
  111. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_tui_paste_placeholder.py +46 -1
  112. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_tui_queue_sdk_source.py +35 -11
  113. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_tui_team_messages.py +0 -2
  114. comate_cli-0.7.0/tests/test_tui_thinking_display.py +71 -0
  115. comate_cli-0.7.0/tests/test_tui_tool_result_registry_lifecycle.py +96 -0
  116. {comate_cli-0.6.2 → comate_cli-0.7.0}/uv.lock +42 -9
  117. comate_cli-0.6.2/README.md +0 -16
  118. comate_cli-0.6.2/comate_cli/terminal_agent/markdown_render.py +0 -26
  119. comate_cli-0.6.2/comate_cli/terminal_agent/resume_selector.py +0 -284
  120. comate_cli-0.6.2/comate_cli/terminal_agent/rewind_store.py +0 -744
  121. comate_cli-0.6.2/comate_cli/terminal_agent/session_view.py +0 -100
  122. comate_cli-0.6.2/tests/test_completion_context_activation.py +0 -56
  123. comate_cli-0.6.2/tests/test_markdown_render.py +0 -53
  124. comate_cli-0.6.2/tests/test_mention_completer.py +0 -389
  125. comate_cli-0.6.2/tests/test_plugin_slash_commands.py +0 -17
  126. comate_cli-0.6.2/tests/test_resume_selector.py +0 -167
  127. comate_cli-0.6.2/tests/test_rewind_command_semantics.py +0 -129
  128. comate_cli-0.6.2/tests/test_rewind_store.py +0 -309
  129. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/__init__.py +0 -0
  130. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/__main__.py +0 -0
  131. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/mcp_cli.py +0 -0
  132. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/__init__.py +0 -0
  133. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/assistant_render.py +0 -0
  134. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/codenames.py +0 -0
  135. {comate_cli-0.6.2/comate_cli/terminal_agent/plugins → comate_cli-0.7.0/comate_cli/terminal_agent/config}/__init__.py +0 -0
  136. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/custom_slash_commands.py +0 -0
  137. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/env_utils.py +0 -0
  138. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/figures.py +0 -0
  139. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/fragment_utils.py +0 -0
  140. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/input_geometry.py +0 -0
  141. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/layout_coordinator.py +0 -0
  142. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/logo.py +0 -0
  143. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/message_style.py +0 -0
  144. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/path_context_hint.py +0 -0
  145. {comate_cli-0.6.2/comate_cli/terminal_agent/plugins/components → comate_cli-0.7.0/comate_cli/terminal_agent/plugins}/__init__.py +0 -0
  146. {comate_cli-0.6.2/comate_cli/terminal_agent/plugins/tabs → comate_cli-0.7.0/comate_cli/terminal_agent/plugins/components}/__init__.py +0 -0
  147. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/plugins/components/detail_view.py +0 -0
  148. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/plugins/components/plugin_list.py +0 -0
  149. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/plugins/components/search_box.py +0 -0
  150. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/plugins/components/tab_bar.py +0 -0
  151. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/plugins/marketplace_install_view.py +0 -0
  152. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/plugins/plugin_picker.py +0 -0
  153. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/plugins/tabs/discover_tab.py +0 -0
  154. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/plugins/tabs/installed_tab.py +0 -0
  155. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/plugins/tabs/marketplaces_tab.py +0 -0
  156. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/rpc_protocol.py +0 -0
  157. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/rpc_stdio.py +0 -0
  158. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/selection_menu.py +0 -0
  159. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/startup.py +0 -0
  160. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/status_bar.py +0 -0
  161. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/text_effects.py +0 -0
  162. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/tui_parts/__init__.py +0 -0
  163. {comate_cli-0.6.2 → comate_cli-0.7.0}/comate_cli/terminal_agent/tui_parts/slash_command_registry.py +0 -0
  164. {comate_cli-0.6.2 → comate_cli-0.7.0}/docs/superpowers/plans/2026-04-03-phrase-shuffle.md +0 -0
  165. {comate_cli-0.6.2 → comate_cli-0.7.0}/docs/superpowers/specs/2026-04-01-conditional-diff-subtitle-design.md +0 -0
  166. {comate_cli-0.6.2 → comate_cli-0.7.0}/docs/superpowers/specs/2026-04-03-phrase-shuffle-design.md +0 -0
  167. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/conftest.py +0 -0
  168. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_animator_shuffle.py +0 -0
  169. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_app_mcp_preload.py +0 -0
  170. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_app_preflight_gate.py +0 -0
  171. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_app_print_mode.py +0 -0
  172. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_app_usage_line.py +0 -0
  173. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_cli_project_root.py +0 -0
  174. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_context_command.py +0 -0
  175. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_custom_slash_commands.py +0 -0
  176. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_errors_tab.py +0 -0
  177. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_event_renderer_boundary.py +0 -0
  178. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_event_renderer_e2e.py +0 -0
  179. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_input_behavior.py +0 -0
  180. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_input_history.py +0 -0
  181. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_layout_coordinator.py +0 -0
  182. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_logo.py +0 -0
  183. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_marketplaces_tab.py +0 -0
  184. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_path_context_hint.py +0 -0
  185. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_question_key_bindings.py +0 -0
  186. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_question_view.py +0 -0
  187. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_rpc_protocol.py +0 -0
  188. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_rpc_stdio_bridge.py +0 -0
  189. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_selection_menu.py +0 -0
  190. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_slash_argument_hint.py +0 -0
  191. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_slash_completer.py +0 -0
  192. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_slash_registry.py +0 -0
  193. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_status_bar.py +0 -0
  194. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_status_bar_transient.py +0 -0
  195. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_task_panel_format.py +0 -0
  196. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_task_poll.py +0 -0
  197. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_tui_elapsed_status.py +0 -0
  198. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_tui_mcp_init_gate.py +0 -0
  199. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_tui_queue_preview.py +0 -0
  200. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_tui_split_invariance.py +0 -0
  201. {comate_cli-0.6.2 → comate_cli-0.7.0}/tests/test_update_check.py +0 -0
@@ -5,6 +5,8 @@ __pycache__/
5
5
  .agent
6
6
  .agents
7
7
  set_env.sh
8
+ .vscode/
9
+ codex/
8
10
  .worktrees/
9
11
  # C extensions
10
12
  *.so
@@ -227,3 +229,7 @@ __marimo__/
227
229
  .DS_Store
228
230
  .DS_Store.worktrees/
229
231
  .superpowers/
232
+ .codex/
233
+
234
+ # pnpm
235
+ .pnpm-store/
@@ -0,0 +1,115 @@
1
+ ## global rule
2
+ 1. 涉及到 Prompt_toolkit 这个TUI框架, 你必须使用 context7 mcp里面的 query-docs和resolve-library-id 功能来查询相关文档, 以确保你对这个框架的理解是正确的. this is MUST
3
+ 2. 总是使用中文编写详细的git commit message
4
+
5
+ ## 12条黄金规则
6
+
7
+ 这些规则适用于本项目中的每一个任务,除非被明确覆盖。核心原则:非简单任务宁可谨慎,不要图快。简单任务可以灵活处理。
8
+
9
+ 1. 动手之前先动脑
10
+ 把你的假设说出来。不确定就问,别猜。遇到模糊需求时,列出多种可能的理解。如果有更简单的方案,要敢于提出来。搞不清楚的时候就停下来,把不清楚的点明确说出来。
11
+
12
+ 2. 简单至上
13
+ 写能解决问题的最少代码,不写投机性代码。不加需求以外的功能。一次性代码不搞抽象封装。自测标准:一个资深工程师看了会不会觉得过度设计?如果会,就简化。
14
+
15
+ 3. 精准修改,不多动
16
+ 只改必须改的地方。只清理自己弄乱的部分。不要顺手"优化"旁边的代码、注释或格式。没坏的东西不要重构。风格跟已有代码保持一致。
17
+
18
+ 4. 以目标驱动执行
19
+ 先定义成功标准,然后循环迭代直到验证通过。不要机械地按步骤走,而是盯着目标不断调整。有了清晰的成功标准,你才能自主地持续迭代。
20
+
21
+ 5. 模型只用于需要判断力的场景
22
+ 适合用 AI 做的事:分类、起草、总结、信息提取。不该用 AI 做的事:路由分发、重试逻辑、确定性转换。如果写代码就能搞定,就用代码搞定。
23
+
24
+ 6. 核实优先于推断(冰山法则)
25
+ 你在上下文中看到的信息只是冰山一角,不能代替真实验证。
26
+ 判断标准不是"我有没有说出猜测的话",而是"我这个结论背后有没有本轮刚验证过的事实支撑"——只要要陈述文件/函数/配置内容或修改代码,却没有刚读过、刚确认过,就是在猜,必须先验证再继续。
27
+
28
+ 7. 遇到冲突要选边,不要和稀泥
29
+ 两种模式互相矛盾时,选一个(优先选更新的或经过更多验证的),说明理由,把另一个标记为待清理。不要把两种冲突的模式混在一起用。
30
+
31
+ 8. 先读再写
32
+ 写新代码之前,先读清楚现有的导出接口、直接调用方、公共工具函数。"看起来互不相关"这种判断很危险。如果不理解代码为什么这样写,先问。
33
+
34
+ 9. 测试要验证意图,不只是行为
35
+ 测试必须体现某个行为**为什么**重要,而不仅仅是**做了什么**。如果业务逻辑变了,测试却不会挂,那这个测试就是有问题的。
36
+
37
+ 10. 每完成一个重要步骤就做检查点
38
+ 总结一下:做了什么、验证了什么、还剩什么。不要在一个自己都说不清楚的状态上继续推进。如果失去了方向感,停下来,重新陈述当前状态。
39
+
40
+ 11. 跟着项目的既有约定走,哪怕你不认同
41
+ 在项目内部,一致性 > 个人偏好。如果你真觉得某个约定有害,把问题提出来讨论,但不要悄悄另搞一套。
42
+
43
+ 12. 有问题就大声说
44
+ 如果有东西被悄悄跳过了,不能说"已完成"。如果有测试被跳过了,不能说"测试全过"。默认立场是把不确定性暴露出来,而不是藏起来。
45
+
46
+
47
+ ## python coding rule
48
+ 1. 代码必须使用 logging 模块进行日志记录,禁止使用 print 语句
49
+ 2. 运行 python必须使用 uv run python 脚本名.py 的方式运行
50
+ 3. 安装pip包必须使用 uv add 包名 的方式安装
51
+
52
+ ## Context 重建铁律
53
+ comate_agent_sdk 的上下文重建规则是高优先级架构约束。涉及 static header / session_state / conversation 三层分离、header_snapshot 的 first-snapshot-wins 语义、resume / fork / compact / clear_history 各自的不变量,以及最低测试护栏清单。
54
+ 任何改动涉及 chat_session.py、context/ir.py、context/compaction.py、agent/history.py、agent/init.py、agent/setup.py 时 MUST 先阅读完整规则。
55
+ → ./rules/context-rebuild.md
56
+
57
+ ## Token 账本语义铁律
58
+
59
+ comate_agent_sdk 的 token 相关概念分属不同账本,后续改动 MUST 先阅读完整规则,禁止为了修某个阈值或展示问题把不同账本混算。
60
+ → ./rules/token-accounting.md
61
+
62
+ ## TUI 编程铁律
63
+ 不做 History TUI,历史只写 scrollback。layout 只允许底部交互带。颜色策略以 shell 背景为准。包含 Team 事件显示铁律和 Pre-flight TUI 铁律。
64
+ 涉及 prompt_toolkit layout、team 事件渲染、独立 Application 弹窗时 MUST 先阅读完整规则。
65
+ → ./rules/tui.md
66
+
67
+ ## 代码放置规则
68
+
69
+ 当需要为某个模块添加新功能时,先判断:这个功能是该模块的 **核心职责** 还是 **业务扩展**?
70
+
71
+ - **核心职责**:直接操作该模块自身管理的数据结构 → 可以加方法
72
+ - **业务扩展**:借用该模块的 API 完成上层业务目标 → 在调用方实现,禁止侵入
73
+
74
+ 判断标准:如果删掉这个方法,该模块的核心抽象(数据结构 + 主流程)是否仍然完整?
75
+ 如果仍然完整,说明这个方法不属于这里。
76
+
77
+ 实践要求:
78
+ 1. 新增方法前,说明它属于核心职责还是业务扩展
79
+ 2. 如果是业务扩展,必须放在调用方或独立 service 中,通过公共 API 操作
80
+ 3. 单个模块超过 500 行时,主动审视是否有职责泄漏
81
+
82
+ ## Prompt 工程规则
83
+
84
+ 涉及 SDK 内置 prompt / 提示词时,后续 agent MUST 默认遵循以下约定,禁止重新回到“局部硬编码 + 局部替换”的分散实现。
85
+
86
+ - SDK 自带 prompt 资源 MUST 放在 `comate_agent_sdk/prompts/` 下,按领域分目录,例如 `agent/`、`subagent/`。
87
+ - 新增 prompt 时,优先判断它是 **SDK 静态资源** 还是 **调用方临时拼接文本**:
88
+ - SDK 静态资源:放入 `comate_agent_sdk/prompts/`,通过统一 loader 读取。
89
+ - 调用方临时拼接文本:保留在调用方,禁止为了“统一”硬塞进不相关模块。
90
+ - MUST 使用两阶段接口:
91
+ - `load_prompt()` 只负责读取包内 markdown 资源。
92
+ - `render_prompt()` 只负责变量渲染。
93
+ - 新 prompt 的变量语法 MUST 统一使用 `{{VAR_NAME}}`,不要新增 `{var}`、`%s`、手写 `replace()` 等新风格。
94
+
95
+ ## System Tool 描述工程规则
96
+
97
+ 涉及 SDK 内置 system tool 的 schema description / system prompt usage_rules / 默认上限值时,
98
+ 后续 agent MUST 默认遵循以下约定。
99
+
100
+ → ./rules/system-tool-prompt-conventions.md
101
+
102
+ ## Tool Envelope 与 Typed Schema 铁律
103
+
104
+ comate_agent_sdk 内置 system tool 的"信封 (`ok/err` 6-key envelope) + typed OutputSchema + `ToolMessage.raw_envelope` 三轨分离"是 typed `ToolResultEvent.output`、SDK 投影、resume 持久化、reminder 引擎共同依赖的事实契约。envelope 一旦被静默改形 / 截断 / 污染框架字段,会让 typed 事件、投影、reminder、jsonl resume **同时静默失效**。
105
+
106
+ 涉及以下任一项 MUST 先阅读完整规则,禁止仅"局部 patch":
107
+
108
+ - `comate_agent_sdk/system_tools/tool_result.py`、`tool_schemas/`(含 `_registry.py` / `_projection.py` / `_base.py` / 各 `<tool>.py`)、`system_tools/tools|task|team/` 下 system tool 的返回结构。
109
+ - `agent/tool_exec.py` 中 `_build_tool_result_message()` / `_extract_tool_envelope()`、`llm/messages.py` 中 `ToolMessage` 的 `content` / `raw_envelope` / `execution_meta` 字段、`mcp/manager.py` 中 `McpToolResult`。
110
+ - `agent/runner_engine/execution/tool_execution.py` 的 `_resolve_tool_output()`、`runner_engine/projection/tool_result_projection.py`、`agent/events.py` 中 `ToolResultEvent.output`、`context/reminder_engine.py`、`subagent/team_session.py`、`chat_session.py` resume 路径。
111
+ - size guard / truncation / artifact spill 与 envelope 关系的任何 plan。
112
+
113
+ 核心铁律一句话:**system tool 必须 `ok()/err()` envelope;envelope 形状不可破坏性变更;envelope 是 typed schema 唯一原料;envelope 只装业务字段,框架元数据走 `execution_meta`;envelope 严禁 slim / 入 prompt / 被 monkey-patch 注册;MCP 用 `McpToolResult.raw_result`、不混 `raw_envelope`;用户 `@tool` 永远是 `CustomToolOutput`。**
114
+
115
+ → ./rules/tool-envelope.md
@@ -0,0 +1,50 @@
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.7.0a1 - 2026-05-19 (alpha)
32
+
33
+ > 这是一个 **alpha 预发布版本**,用于内部 / 早期测试。
34
+ > `pip install comate-cli` 默认不会安装到本版本,需要显式 `pip install comate-cli==0.7.0a1` 或 `pip install --pre comate-cli`。
35
+
36
+ ### Breaking Changes
37
+
38
+ - 依赖 `comate-agent-sdk>=0.8.0a1`,与旧 SDK 不兼容。
39
+ - 旧版本生成的 resume jsonl / context 快照不保证可以正常回放,**建议从空 session 重新开始**。
40
+
41
+ ### 累积变更
42
+
43
+ 自 0.6.x 起累积大量 TUI 与会话能力改进,涵盖:
44
+
45
+ - Resume picker 重做:搜索、跨 cwd 切换、预览模式、视觉抛光
46
+ - Auto compact 期间显式提示 `Compacting context`
47
+ - Team / subagent 事件透出与渲染统一
48
+ - 与 SDK 0.8.0a2 配套的 tool envelope / token 账本展示
49
+
50
+ 详细变更请参考 git log。
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: comate-cli
3
- Version: 0.6.2
3
+ Version: 0.7.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
@@ -15,7 +15,7 @@ Classifier: Programming Language :: Python :: 3.12
15
15
  Classifier: Programming Language :: Python :: 3.13
16
16
  Requires-Python: >=3.11
17
17
  Requires-Dist: charset-normalizer==3.4.7
18
- Requires-Dist: comate-agent-sdk<1.0.0,>=0.0.2
18
+ Requires-Dist: comate-agent-sdk<0.9.0,>=0.8.0a1
19
19
  Requires-Dist: concurrent-log-handler>=0.9.25
20
20
  Requires-Dist: curl-cffi==0.13.0
21
21
  Requires-Dist: packaging>=21.0
@@ -41,3 +41,28 @@ Or install globally:
41
41
  uv tool install comate-cli
42
42
  comate
43
43
  ```
44
+
45
+ ## 排错 / Troubleshooting
46
+
47
+ ### 看不到错误信息但行为异常
48
+
49
+ comate 在生产模式下会把若干无害但吵闹的 shutdown 噪音写入
50
+ `~/.comate/logs/agent.log`,而不是显示在终端里。这包括 Windows asyncio
51
+ shutdown 的 `ResourceWarning`、MCP 服务器违规向 stdout 写日志导致的 JSONRPC
52
+ 解析失败,以及解释器退出阶段的 unraisable 异常。
53
+
54
+ 如果你怀疑隐藏了真实错误,可以按下面顺序排查:
55
+
56
+ ```bash
57
+ # 1. 看 noise 通道的最近 50 条
58
+ grep -E "\[unraisable\]|\[excepthook\]|\[warning\]" ~/.comate/logs/agent.log | tail -50
59
+
60
+ # 2. 看完整 agent.log 末尾
61
+ tail -200 ~/.comate/logs/agent.log
62
+
63
+ # 3. 临时关掉噪音接管器,看原始输出(开发场景)
64
+ COMATE_DEBUG_NOISE=1 comate
65
+ ```
66
+
67
+ `COMATE_DEBUG_NOISE=1` 设置后,`_ShutdownNoiseGuard` 会跳过 install,
68
+ 错误会像旧版本一样直接打印到 stderr。
@@ -0,0 +1,41 @@
1
+ # comate-cli
2
+
3
+ Comate terminal CLI package.
4
+
5
+ ## Usage
6
+
7
+ ```bash
8
+ uvx comate-cli
9
+ ```
10
+
11
+ Or install globally:
12
+
13
+ ```bash
14
+ uv tool install comate-cli
15
+ comate
16
+ ```
17
+
18
+ ## 排错 / Troubleshooting
19
+
20
+ ### 看不到错误信息但行为异常
21
+
22
+ comate 在生产模式下会把若干无害但吵闹的 shutdown 噪音写入
23
+ `~/.comate/logs/agent.log`,而不是显示在终端里。这包括 Windows asyncio
24
+ shutdown 的 `ResourceWarning`、MCP 服务器违规向 stdout 写日志导致的 JSONRPC
25
+ 解析失败,以及解释器退出阶段的 unraisable 异常。
26
+
27
+ 如果你怀疑隐藏了真实错误,可以按下面顺序排查:
28
+
29
+ ```bash
30
+ # 1. 看 noise 通道的最近 50 条
31
+ grep -E "\[unraisable\]|\[excepthook\]|\[warning\]" ~/.comate/logs/agent.log | tail -50
32
+
33
+ # 2. 看完整 agent.log 末尾
34
+ tail -200 ~/.comate/logs/agent.log
35
+
36
+ # 3. 临时关掉噪音接管器,看原始输出(开发场景)
37
+ COMATE_DEBUG_NOISE=1 comate
38
+ ```
39
+
40
+ `COMATE_DEBUG_NOISE=1` 设置后,`_ShutdownNoiseGuard` 会跳过 install,
41
+ 错误会像旧版本一样直接打印到 stderr。
@@ -3,9 +3,11 @@ from __future__ import annotations
3
3
  import atexit
4
4
  import asyncio
5
5
  import logging
6
+ import os
6
7
  import signal
7
8
  import subprocess
8
9
  import sys
10
+ import warnings
9
11
  try:
10
12
  import termios
11
13
  except ImportError:
@@ -68,10 +70,21 @@ class _ShutdownNoiseGuard:
68
70
 
69
71
  def __init__(self) -> None:
70
72
  self._shutdown_armed = False
73
+ self._installed = False
71
74
  self._orig_unraisablehook = sys.unraisablehook
75
+ self._orig_excepthook = sys.excepthook
76
+ self._orig_showwarning = warnings.showwarning
77
+ self._noise_logger = self._build_noise_logger()
72
78
 
73
79
  def install(self) -> None:
80
+ if self._installed:
81
+ return
82
+ if os.environ.get("COMATE_DEBUG_NOISE", "").strip().lower() in ("1", "true", "yes"):
83
+ return
74
84
  sys.unraisablehook = self._unraisablehook
85
+ sys.excepthook = self._excepthook
86
+ warnings.showwarning = self._showwarning
87
+ self._installed = True
75
88
 
76
89
  def begin_shutdown(self) -> None:
77
90
  if self._shutdown_armed:
@@ -88,13 +101,94 @@ class _ShutdownNoiseGuard:
88
101
  except Exception as exc:
89
102
  logger.debug(f"Failed to switch SIGINT to SIG_IGN during shutdown: {exc}")
90
103
 
104
+ def _route(
105
+ self,
106
+ channel: str,
107
+ *,
108
+ exc: BaseException | None = None,
109
+ msg: str = "",
110
+ **extra: object,
111
+ ) -> None:
112
+ try:
113
+ if exc is not None:
114
+ self._noise_logger.warning(
115
+ "[%s] %s: %s",
116
+ channel,
117
+ type(exc).__name__,
118
+ exc,
119
+ exc_info=(type(exc), exc, exc.__traceback__),
120
+ extra={"noise_channel": channel, **extra},
121
+ )
122
+ else:
123
+ self._noise_logger.warning(
124
+ "[%s] %s",
125
+ channel,
126
+ msg,
127
+ extra={"noise_channel": channel, **extra},
128
+ )
129
+ except Exception:
130
+ pass
131
+
91
132
  def _unraisablehook(self, unraisable: object) -> None:
92
133
  exc_value = getattr(unraisable, "exc_value", None)
93
134
  if self._shutdown_armed and isinstance(exc_value, KeyboardInterrupt):
94
- logger.debug("Suppressed unraisable KeyboardInterrupt during shutdown")
95
135
  return
136
+ obj = getattr(unraisable, "object", None)
137
+ self._route("unraisable", exc=exc_value, object_repr=repr(obj))
138
+
139
+ def _excepthook(
140
+ self,
141
+ exc_type: type[BaseException],
142
+ exc_value: BaseException,
143
+ exc_tb: object,
144
+ ) -> None:
145
+ self._route("excepthook", exc=exc_value)
146
+
147
+ def _showwarning(
148
+ self,
149
+ message: Warning | str,
150
+ category: type[Warning],
151
+ filename: str,
152
+ lineno: int,
153
+ file: object | None = None,
154
+ line: str | None = None,
155
+ ) -> None:
156
+ self._route(
157
+ "warning",
158
+ msg=str(message),
159
+ category=category.__name__,
160
+ warning_filename=str(filename),
161
+ warning_lineno=lineno,
162
+ )
96
163
 
97
- self._orig_unraisablehook(unraisable)
164
+ def _build_noise_logger(self) -> logging.Logger:
165
+ log = logging.getLogger("comate.noise")
166
+ log.setLevel(logging.WARNING)
167
+ log.propagate = False
168
+ if log.handlers:
169
+ return log
170
+
171
+ try:
172
+ from concurrent_log_handler import ConcurrentRotatingFileHandler
173
+
174
+ log_dir = os.path.join(os.path.expanduser("~"), ".comate", "logs")
175
+ os.makedirs(log_dir, exist_ok=True)
176
+ handler = ConcurrentRotatingFileHandler(
177
+ os.path.join(log_dir, "agent.log"),
178
+ maxBytes=10 * 1024 * 1024,
179
+ backupCount=3,
180
+ encoding="utf-8",
181
+ )
182
+ handler.setFormatter(
183
+ logging.Formatter(
184
+ "%(asctime)s %(levelname)s %(name)s [%(noise_channel)s]: %(message)s"
185
+ )
186
+ )
187
+ log.addHandler(handler)
188
+ except Exception:
189
+ log.addHandler(logging.NullHandler())
190
+
191
+ return log
98
192
 
99
193
 
100
194
  def _usage_text() -> str:
@@ -153,6 +247,10 @@ def _parse_args(argv: list[str]) -> tuple[bool, str | None, bool, str | None]:
153
247
  def main(argv: list[str] | None = None) -> None:
154
248
  run_argv = list(argv) if argv is not None else sys.argv[1:]
155
249
 
250
+ if run_argv in (["--help"], ["-h"]):
251
+ sys.stdout.write(f"{_usage_text()}\n")
252
+ return
253
+
156
254
  if run_argv and run_argv[0] == "mcp":
157
255
  from comate_cli.mcp_cli import McpCliError, run_mcp_command
158
256
 
@@ -9,7 +9,7 @@ from enum import Enum
9
9
  from rich.console import RenderableType
10
10
  from rich.text import Text
11
11
 
12
- from comate_agent_sdk.agent.events import StopEvent, TextEvent, ToolCallEvent, ToolResultEvent, UserQuestionEvent
12
+ from comate_agent_sdk.agent.events import StopEvent, ToolCallEvent, ToolResultEvent, UserQuestionEvent
13
13
 
14
14
  from comate_cli.terminal_agent.figures import (
15
15
  BREATH_DOT_GLYPHS as _FIGURES_BREATH_DOT_GLYPHS,
@@ -238,6 +238,25 @@ BREATH_DOT_COLORS: tuple[str, ...] = (
238
238
  )
239
239
  BREATH_DOT_GLYPHS: tuple[str, ...] = _FIGURES_BREATH_DOT_GLYPHS
240
240
 
241
+ LOADING_SPINNER_FRAMES: tuple[str, ...] = (
242
+ "⠋",
243
+ "⠙",
244
+ "⠹",
245
+ "⠸",
246
+ "⠼",
247
+ "⠴",
248
+ "⠦",
249
+ "⠧",
250
+ "⠇",
251
+ "⠏",
252
+ )
253
+
254
+ HIDDEN_THINKING_BADGES: tuple[str, ...] = (
255
+ "ultra thinking",
256
+ "deep thinking",
257
+ "extended thinking"
258
+ )
259
+
241
260
 
242
261
  def breathing_dot_color(frame: int) -> str:
243
262
  """Return the breathing dot color for a given animation frame."""
@@ -252,6 +271,11 @@ def breathing_dot_glyph(now_monotonic: float | None = None) -> str:
252
271
  return BREATH_DOT_GLYPHS[phase]
253
272
 
254
273
 
274
+ def loading_spinner_glyph(frame: int) -> str:
275
+ """Return the compact spinner glyph for the main loading line."""
276
+ return LOADING_SPINNER_FRAMES[frame % len(LOADING_SPINNER_FRAMES)]
277
+
278
+
255
279
  def _lerp_rgb(
256
280
  start_rgb: tuple[int, int, int],
257
281
  end_rgb: tuple[int, int, int],
@@ -385,11 +409,9 @@ class SubmissionAnimator:
385
409
  return Text("")
386
410
 
387
411
  phrase = self._status_hint if self._status_hint else (self._shuffled[self._phrase_idx] + ELLIPSIS)
388
- dot_color = breathing_dot_color(self._frame)
389
- now_monotonic = time.monotonic()
390
412
  dot = Text(
391
- f"{breathing_dot_glyph(now_monotonic)} ",
392
- style=f"bold {dot_color}",
413
+ f"{loading_spinner_glyph(self._frame)} ",
414
+ style="bold #9CA3AF",
393
415
  )
394
416
  sweep = _cyan_sweep_text(phrase, frame=self._frame)
395
417
  return Text.assemble(dot, sweep)
@@ -186,14 +186,23 @@ async def add(a: int, b: int) -> int:
186
186
 
187
187
 
188
188
  def _build_agent(*, project_root: Path | None = None) -> Agent:
189
+ from comate_agent_sdk.agent.compaction import CompactionConfig
190
+ from comate_cli.terminal_agent.config import store
191
+
189
192
  resolved_project_root = project_root or _resolve_cli_project_root()
193
+ snapshot = store.load()
190
194
 
191
195
  return Agent(
192
196
  config=AgentConfig(
193
197
  role="software_engineering",
194
198
  cwd=resolved_project_root,
195
199
  env_options=EnvOptions(system_env=True, git_env=True),
196
- use_streaming_task=True, # 启用流式 Task(实时显示嵌套工具调用)
200
+ use_streaming_task=True,
201
+ memory_background_enabled=snapshot.memory_background_enabled,
202
+ memory_dreaming_enabled=snapshot.memory_dreaming_enabled,
203
+ compaction=CompactionConfig(
204
+ threshold_ratio=snapshot.compaction_threshold_ratio,
205
+ ),
197
206
  )
198
207
  )
199
208
 
@@ -415,11 +424,13 @@ async def run(
415
424
  exc_info=True,
416
425
  )
417
426
  finally:
418
- logging_session.close()
419
- if active_session is session:
420
- await _graceful_shutdown(active_session)
421
- else:
422
- await _graceful_shutdown(session, active_session)
427
+ try:
428
+ if active_session is session:
429
+ await _graceful_shutdown(active_session)
430
+ else:
431
+ await _graceful_shutdown(session, active_session)
432
+ finally:
433
+ logging_session.close()
423
434
 
424
435
  if usage_line:
425
436
  console.print(f"[dim]{usage_line}[/]")
@@ -0,0 +1,52 @@
1
+ """ConfigSnapshot:/config UI、store、startup 三处共享的偏好快照。
2
+
3
+ Revision 2: 5 项偏好(display_thinking, model_level, compaction, memory_background, memory_dreaming)。
4
+ """
5
+ from __future__ import annotations
6
+
7
+ from dataclasses import dataclass, replace
8
+ from typing import Literal
9
+
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
+
19
+
20
+ @dataclass(frozen=True, slots=True)
21
+ class ConfigSnapshot:
22
+ """5 项 CLI 偏好的不可变快照。"""
23
+
24
+ display_thinking: bool = False
25
+ model_level: ModelLevelPreset = "MID"
26
+ compaction_threshold_ratio: float = 0.80
27
+ compaction_threshold_preset: ThresholdPreset = "balanced"
28
+ memory_background_enabled: bool = True
29
+ memory_dreaming_enabled: bool = True
30
+
31
+ @classmethod
32
+ def default(cls) -> "ConfigSnapshot":
33
+ return cls()
34
+
35
+ def with_display_thinking(self, enabled: bool) -> "ConfigSnapshot":
36
+ return replace(self, display_thinking=enabled)
37
+
38
+ def with_model_level(self, level: ModelLevelPreset) -> "ConfigSnapshot":
39
+ return replace(self, model_level=level)
40
+
41
+ def with_threshold_preset(self, preset: ThresholdPreset) -> "ConfigSnapshot":
42
+ return replace(
43
+ self,
44
+ compaction_threshold_preset=preset,
45
+ compaction_threshold_ratio=COMPACT_PRESETS[preset],
46
+ )
47
+
48
+ def with_memory_background(self, enabled: bool) -> "ConfigSnapshot":
49
+ return replace(self, memory_background_enabled=enabled)
50
+
51
+ def with_memory_dreaming(self, enabled: bool) -> "ConfigSnapshot":
52
+ return replace(self, memory_dreaming_enabled=enabled)