soothe-cli 0.6.13__tar.gz → 0.6.14__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 (113) hide show
  1. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/PKG-INFO +1 -1
  2. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/textual_adapter.py +23 -0
  3. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/widgets/messages.py +70 -18
  4. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/.gitignore +0 -0
  5. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/README.md +0 -0
  6. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/pyproject.toml +0 -0
  7. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/__init__.py +0 -0
  8. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/cli/__init__.py +0 -0
  9. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/cli/commands/__init__.py +0 -0
  10. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/cli/commands/autopilot_cmd.py +0 -0
  11. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/cli/commands/cron_cmd.py +0 -0
  12. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/cli/commands/loop_cmd.py +0 -0
  13. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/cli/commands/run_cmd.py +0 -0
  14. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/cli/commands/status_cmd.py +0 -0
  15. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/cli/execution/__init__.py +0 -0
  16. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/cli/execution/daemon.py +0 -0
  17. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/cli/execution/daemon_errors.py +0 -0
  18. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/cli/execution/headless.py +0 -0
  19. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/cli/execution/headless_renderer.py +0 -0
  20. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/cli/execution/launcher.py +0 -0
  21. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/cli/main.py +0 -0
  22. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/config/__init__.py +0 -0
  23. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/config/cli_config.py +0 -0
  24. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/config/loader.py +0 -0
  25. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/config/logging_setup.py +0 -0
  26. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/runtime/__init__.py +0 -0
  27. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/runtime/headless/processor.py +0 -0
  28. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/runtime/headless/processor_state.py +0 -0
  29. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/runtime/parse/_utils.py +0 -0
  30. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/runtime/parse/message_processing.py +0 -0
  31. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/runtime/parse/tool_call_resolution.py +0 -0
  32. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/runtime/parse/tool_message_format.py +0 -0
  33. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/runtime/parse/tool_result.py +0 -0
  34. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/runtime/policy/display_policy.py +0 -0
  35. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/runtime/policy/essential_events.py +0 -0
  36. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/runtime/policy/tui_trace_log.py +0 -0
  37. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/runtime/presentation/duration_format.py +0 -0
  38. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/runtime/presentation/engine.py +0 -0
  39. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/runtime/presentation/explore_task_display.py +0 -0
  40. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/runtime/presentation/renderer_base.py +0 -0
  41. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/runtime/presentation/renderer_protocol.py +0 -0
  42. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/runtime/state/file_tracker.py +0 -0
  43. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/runtime/state/session_stats.py +0 -0
  44. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/runtime/state/step_router.py +0 -0
  45. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/runtime/state/stream_accumulator.py +0 -0
  46. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/runtime/state/transcript.py +0 -0
  47. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/runtime/transport/session.py +0 -0
  48. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/runtime/turn/pipeline.py +0 -0
  49. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/runtime/turn/prepare.py +0 -0
  50. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/runtime/wire/chunk_filter.py +0 -0
  51. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/runtime/wire/display_text.py +0 -0
  52. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/runtime/wire/message_text.py +0 -0
  53. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/runtime/wire/messages.py +0 -0
  54. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/__init__.py +0 -0
  55. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/_cli_context.py +0 -0
  56. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/_env_vars.py +0 -0
  57. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/_version.py +0 -0
  58. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/app/__init__.py +0 -0
  59. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/app/_app.py +0 -0
  60. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/app/_commands.py +0 -0
  61. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/app/_execution.py +0 -0
  62. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/app/_history.py +0 -0
  63. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/app/_messages_mixin.py +0 -0
  64. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/app/_model.py +0 -0
  65. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/app/_module_init.py +0 -0
  66. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/app/_startup.py +0 -0
  67. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/app/_ui.py +0 -0
  68. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/app/app.tcss +0 -0
  69. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/binding.py +0 -0
  70. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/command_registry.py +0 -0
  71. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/commands/__init__.py +0 -0
  72. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/commands/command_router.py +0 -0
  73. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/commands/slash_commands.py +0 -0
  74. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/commands/subagent_routing.py +0 -0
  75. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/config.py +0 -0
  76. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/file_change_notify.py +0 -0
  77. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/file_change_renderers.py +0 -0
  78. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/hooks.py +0 -0
  79. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/input.py +0 -0
  80. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/media_utils.py +0 -0
  81. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/model_config.py +0 -0
  82. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/path_utils.py +0 -0
  83. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/preview_limits.py +0 -0
  84. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/project_utils.py +0 -0
  85. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/sessions.py +0 -0
  86. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/skills/__init__.py +0 -0
  87. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/skills/invocation.py +0 -0
  88. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/skills/load.py +0 -0
  89. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/theme.py +0 -0
  90. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/tips.py +0 -0
  91. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/tool_display.py +0 -0
  92. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/unicode_security.py +0 -0
  93. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/update_check.py +0 -0
  94. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/widgets/__init__.py +0 -0
  95. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/widgets/_links.py +0 -0
  96. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/widgets/autocomplete.py +0 -0
  97. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/widgets/autopilot_dashboard.py +0 -0
  98. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/widgets/autopilot_screen.py +0 -0
  99. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/widgets/chat_input.py +0 -0
  100. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/widgets/clipboard.py +0 -0
  101. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/widgets/diff.py +0 -0
  102. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/widgets/editor.py +0 -0
  103. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/widgets/file_change_preview.py +0 -0
  104. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/widgets/history.py +0 -0
  105. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/widgets/loading.py +0 -0
  106. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/widgets/loop_selector.py +0 -0
  107. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/widgets/mcp_viewer.py +0 -0
  108. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/widgets/message_store.py +0 -0
  109. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/widgets/model_selector.py +0 -0
  110. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/widgets/notification_settings.py +0 -0
  111. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/widgets/status.py +0 -0
  112. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/widgets/theme_selector.py +0 -0
  113. {soothe_cli-0.6.13 → soothe_cli-0.6.14}/src/soothe_cli/tui/widgets/welcome.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: soothe-cli
3
- Version: 0.6.13
3
+ Version: 0.6.14
4
4
  Summary: Soothe CLI client - communicates with daemon via WebSocket
5
5
  Project-URL: Homepage, https://github.com/mirasoth/soothe
6
6
  Project-URL: Documentation, https://soothe.readthedocs.io
@@ -119,6 +119,9 @@ from soothe_cli.tui.widgets.messages import (
119
119
 
120
120
  logger = logging.getLogger(__name__)
121
121
 
122
+ # IG-504: LLM retry event type for step card status display
123
+ LLM_RETRY_ATTEMPT = "soothe.cognition.llm.retry.attempt"
124
+
122
125
  # ---------------------------------------------------------------------------
123
126
  # Adapter core
124
127
  # ---------------------------------------------------------------------------
@@ -2793,6 +2796,26 @@ async def execute_task_textual(
2793
2796
  )
2794
2797
  continue
2795
2798
 
2799
+ # IG-504: Handle LLM retry events for step card status display
2800
+ if event_type == LLM_RETRY_ATTEMPT:
2801
+ # Find the running step card for this thread and update retry status
2802
+ attempt = int(data.get("attempt", 0))
2803
+ max_attempts = int(data.get("max_attempts", 0))
2804
+ error_type = str(data.get("error_type", "timeout"))
2805
+ # Try to find a running step card via namespace or active steps
2806
+ widget = None
2807
+ if ns_key:
2808
+ widget = adapter._step_by_namespace.get(ns_key)
2809
+ if widget is None:
2810
+ # Fallback: find any running step in current step messages
2811
+ for step_widget in adapter._current_step_messages.values():
2812
+ if step_widget._status == "running": # noqa: SLF001
2813
+ widget = step_widget
2814
+ break
2815
+ if widget is not None:
2816
+ widget.set_retry_status(attempt, max_attempts, error_type)
2817
+ continue
2818
+
2796
2819
  if event_type == LOOP_REASON_EVENT_TYPE:
2797
2820
  assessment_reasoning = str(
2798
2821
  data.get("assessment_reasoning", "")
@@ -1303,6 +1303,10 @@ class CognitionStepMessage(Vertical):
1303
1303
  self._last_tool_call_count: int = 0
1304
1304
  self._last_summary: str = ""
1305
1305
  self._input_tokens: int = 0
1306
+ # IG-504: Retry tracking for running status display
1307
+ self._retry_attempt: int = 0
1308
+ self._max_retry_attempts: int = 0
1309
+ self._retry_error_type: str | None = None
1306
1310
  self._output_tokens: int = 0
1307
1311
  self._interrupt_message: str | None = None
1308
1312
  self._deferred_interrupted: str | None = None
@@ -2856,14 +2860,53 @@ class CognitionStepMessage(Vertical):
2856
2860
  self._refresh_tools_display()
2857
2861
 
2858
2862
  def _sync_running_status_line(self) -> None:
2859
- """Refresh status text when tool stats change without repainting tool rows."""
2863
+ """Refresh status text when tool stats change without repainting tool rows.
2864
+
2865
+ This method is called when tool calls arrive during a running step,
2866
+ so it updates the status line immediately (without visibility check)
2867
+ to show the tool count in real-time.
2868
+ """
2860
2869
  if self._status == "running":
2861
- self._update_running_animation()
2870
+ self._sync_running_status_text()
2862
2871
  elif self._status == "queued":
2863
2872
  self._refresh_queued_display()
2864
2873
  elif self._status == "pending":
2865
2874
  self._refresh_pending_display()
2866
2875
 
2876
+ def _sync_running_status_text(self) -> None:
2877
+ """Update running status line text immediately (no visibility check).
2878
+
2879
+ Called when tool stats change during running state to show tool count
2880
+ in real-time, bypassing the animation visibility check that would
2881
+ prevent updates for newly-mounted or off-screen widgets.
2882
+ """
2883
+ if self._status != "running" or self._status_widget is None:
2884
+ return
2885
+ frames = get_glyphs().spinner_frames
2886
+ frame = frames[self._spinner_position]
2887
+ elapsed = ""
2888
+ if self._start_time is not None:
2889
+ elapsed_secs = int(time() - self._start_time)
2890
+ elapsed = f" ({format_duration(float(elapsed_secs))})"
2891
+ try:
2892
+ colors = theme.get_theme_colors(self)
2893
+ except Exception: # noqa: BLE001
2894
+ colors = theme.DARK_COLORS
2895
+ gutter = f"{get_glyphs().output_prefix} "
2896
+ stats_suffix = self._stats_title_suffix()
2897
+ token_suffix = self._token_budget_suffix()
2898
+ # IG-504: Show retry count in running status when retries are happening
2899
+ retry_suffix = ""
2900
+ if self._retry_attempt > 0 and self._max_retry_attempts > 0:
2901
+ retry_suffix = f" ({self._retry_attempt}/{self._max_retry_attempts} attempts)"
2902
+ head = f"{gutter}{frame} Running{retry_suffix}...{elapsed}"
2903
+ tail = f"{stats_suffix}{token_suffix}"
2904
+ clear_widget_text_selection(self._status_widget)
2905
+ parts: list[object] = [Content.styled(head, colors.warning)]
2906
+ if tail:
2907
+ parts.append(Content.styled(tail, colors.cognition))
2908
+ self._status_widget.update(Content.assemble(*parts))
2909
+
2867
2910
  def _refresh_pending_display(self) -> None:
2868
2911
  """Show waiting state for planned steps that are not executing yet."""
2869
2912
  if self._status != "pending" or self._status_widget is None:
@@ -2930,29 +2973,38 @@ class CognitionStepMessage(Vertical):
2930
2973
  self._animation_timer.stop()
2931
2974
  self._animation_timer = None
2932
2975
 
2976
+ def set_retry_status(self, attempt: int, max_attempts: int, error_type: str) -> None:
2977
+ """IG-504: Update retry status for running animation display.
2978
+
2979
+ Args:
2980
+ attempt: Current attempt number (1-indexed).
2981
+ max_attempts: Maximum attempts allowed.
2982
+ error_type: "timeout" or "rate_limit".
2983
+ """
2984
+ self._retry_attempt = attempt
2985
+ self._max_retry_attempts = max_attempts
2986
+ self._retry_error_type = error_type
2987
+ # Trigger immediate refresh of running status (bypass visibility check)
2988
+ if self._status == "running":
2989
+ self._sync_running_status_text()
2990
+
2933
2991
  def _update_running_animation(self) -> None:
2992
+ """Animation timer callback: advance spinner and update status line.
2993
+
2994
+ The visibility check here is for efficiency - skipping expensive
2995
+ re-renders when the widget is off-screen. For immediate tool-stats
2996
+ updates (called when tool calls arrive), use `_sync_running_status_text`
2997
+ which bypasses this check.
2998
+ """
2934
2999
  if self._status != "running" or self._status_widget is None:
2935
3000
  return
2936
3001
  if not _is_widget_animation_visible(self):
2937
3002
  return
3003
+ # Advance spinner position for animation effect
2938
3004
  frames = get_glyphs().spinner_frames
2939
- frame = frames[self._spinner_position]
2940
3005
  self._spinner_position = (self._spinner_position + 1) % len(frames)
2941
- elapsed = ""
2942
- if self._start_time is not None:
2943
- elapsed_secs = int(time() - self._start_time)
2944
- elapsed = f" ({format_duration(float(elapsed_secs))})"
2945
- colors = theme.get_theme_colors(self)
2946
- gutter = f"{get_glyphs().output_prefix} "
2947
- stats_suffix = self._stats_title_suffix()
2948
- token_suffix = self._token_budget_suffix()
2949
- head = f"{gutter}{frame} Running...{elapsed}"
2950
- tail = f"{stats_suffix}{token_suffix}"
2951
- clear_widget_text_selection(self._status_widget)
2952
- parts: list[object] = [Content.styled(head, colors.warning)]
2953
- if tail:
2954
- parts.append(Content.styled(tail, colors.cognition))
2955
- self._status_widget.update(Content.assemble(*parts))
3006
+ # Update status line with current stats
3007
+ self._sync_running_status_text()
2956
3008
  if self._has_active_task_branch_animation():
2957
3009
  self._refresh_task_activity_display()
2958
3010
 
File without changes
File without changes
File without changes