soothe-cli 0.8.0__tar.gz → 0.8.1__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.
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/PKG-INFO +2 -1
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/pyproject.toml +2 -1
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/cli/commands/autopilot_cmd.py +2 -2
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/cli/commands/config_cmd.py +1 -1
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/cli/commands/cron_cmd.py +1 -1
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/cli/commands/loop_cmd.py +4 -2
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/cli/commands/run_cmd.py +9 -62
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/cli/commands/status_cmd.py +1 -1
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/cli/execution/daemon.py +6 -47
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/cli/execution/headless.py +1 -1
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/cli/main.py +1 -1
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/config/cli_config.py +1 -1
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/runtime/headless/processor.py +2 -2
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/runtime/headless/processor_state.py +1 -1
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/runtime/parse/tool_result.py +2 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/runtime/presentation/renderer_protocol.py +1 -1
- soothe_cli-0.8.1/src/soothe_cli/runtime/transport/session.py +80 -0
- soothe_cli-0.8.1/src/soothe_cli/runtime/turn/pipeline.py +35 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/runtime/wire/message_text.py +1 -1
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/app/_model.py +3 -2
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/app/_startup.py +2 -2
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/commands/command_router.py +1 -1
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/config.py +1 -1
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/model_config.py +9 -3
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/skills/invocation.py +5 -2
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/theme.py +1 -1
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/tool_display.py +1 -1
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/update_check.py +1 -1
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/chat_input.py +1 -1
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/context_data.py +38 -31
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/context_viewer.py +7 -4
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/messages/cognition_goal_tree.py +1 -1
- soothe_cli-0.8.0/src/soothe_cli/runtime/transport/session.py +0 -773
- soothe_cli-0.8.0/src/soothe_cli/runtime/turn/pipeline.py +0 -404
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/.gitignore +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/README.md +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/__init__.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/cli/__init__.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/cli/commands/__init__.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/cli/execution/__init__.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/cli/execution/daemon_errors.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/cli/execution/headless_renderer.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/cli/execution/launcher.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/config/__init__.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/config/loader.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/config/logging_setup.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/runtime/__init__.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/runtime/parse/message_processing.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/runtime/parse/tool_call_resolution.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/runtime/policy/display_policy.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/runtime/policy/essential_events.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/runtime/policy/tui_trace_log.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/runtime/presentation/duration_format.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/runtime/presentation/engine.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/runtime/presentation/id_format.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/runtime/presentation/renderer_base.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/runtime/presentation/subagent_task_display.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/runtime/state/file_tracker.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/runtime/state/session_stats.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/runtime/state/step_router.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/runtime/state/stream_accumulator.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/runtime/state/transcript.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/runtime/token_events_debug.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/runtime/token_usage.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/runtime/turn/prepare.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/runtime/wire/chunk_filter.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/runtime/wire/messages.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/__init__.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/_cli_context.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/_env_vars.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/_version.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/app/__init__.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/app/_app.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/app/_execution.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/app/_history.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/app/_messages_mixin.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/app/_module_init.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/app/_ui.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/app/app.tcss +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/binding.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/command_registry.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/commands/__init__.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/commands/slash_commands.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/commands/subagent_routing.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/file_change_notify.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/file_change_renderers.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/hooks.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/input.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/markdown_theme.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/media_utils.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/path_utils.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/preview_limits.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/project_utils.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/sessions.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/shell_color.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/skills/__init__.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/skills/load.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/spinner_labels.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/textual_adapter.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/tips.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/unicode_security.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/__init__.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/_links.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/autocomplete.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/clipboard.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/diff.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/editor.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/file_change_preview.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/help_screen.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/history.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/loading.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/loop_selector.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/mcp_viewer.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/message_store.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/messages/__init__.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/messages/_helpers.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/messages/app.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/messages/assistant.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/messages/clarification.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/messages/cognition_reason.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/messages/cognition_step.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/messages/cognition_step_activity.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/messages/cognition_subagent.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/messages/diff_message.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/messages/error.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/messages/skill.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/messages/user.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/model_selector.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/notification_settings.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/plan_quick_view_overlay.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/router_profile_selector.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/status.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/theme_selector.py +0 -0
- {soothe_cli-0.8.0 → soothe_cli-0.8.1}/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.8.
|
|
3
|
+
Version: 0.8.1
|
|
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
|
|
@@ -22,6 +22,7 @@ Requires-Dist: pillow<12.0.0,>=10.0.0
|
|
|
22
22
|
Requires-Dist: python-dotenv<2.0.0,>=1.0.0
|
|
23
23
|
Requires-Dist: pyyaml<7.0.0,>=6.0.0
|
|
24
24
|
Requires-Dist: rich<15.0.0,>=13.0.0
|
|
25
|
+
Requires-Dist: soothe-client-python<1.0.0,>=0.5.25
|
|
25
26
|
Requires-Dist: soothe-sdk<1.0.0,>=0.5.25
|
|
26
27
|
Requires-Dist: textual<10.0.0,>=8.0.0
|
|
27
28
|
Requires-Dist: typer<2.0.0,>=0.9.0
|
|
@@ -23,7 +23,8 @@ classifiers = [
|
|
|
23
23
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
24
24
|
]
|
|
25
25
|
dependencies = [
|
|
26
|
-
"soothe-sdk>=0.5.25,<1.0.0", #
|
|
26
|
+
"soothe-sdk>=0.5.25,<1.0.0", # events/display/wire contracts
|
|
27
|
+
"soothe-client-python>=0.5.25,<1.0.0", # WebSocket transport client
|
|
27
28
|
"typer>=0.9.0,<2.0.0", # CLI framework
|
|
28
29
|
"textual>=8.0.0,<10.0.0", # TUI framework
|
|
29
30
|
"rich>=13.0.0,<15.0.0", # Console output
|
|
@@ -14,12 +14,12 @@ from collections import Counter
|
|
|
14
14
|
from pathlib import Path
|
|
15
15
|
|
|
16
16
|
import typer
|
|
17
|
-
from
|
|
17
|
+
from soothe_client import (
|
|
18
18
|
is_daemon_live,
|
|
19
19
|
websocket_url_from_config,
|
|
20
20
|
ws_command_client_from_config,
|
|
21
21
|
)
|
|
22
|
-
from soothe_sdk.
|
|
22
|
+
from soothe_sdk.wire.protocol import preview_first
|
|
23
23
|
|
|
24
24
|
app = typer.Typer(help="Autopilot mode — long-running autonomous agent control.")
|
|
25
25
|
|
|
@@ -10,7 +10,7 @@ from typing import Annotated, Any
|
|
|
10
10
|
import typer
|
|
11
11
|
from rich.console import Console
|
|
12
12
|
from rich.panel import Panel
|
|
13
|
-
from
|
|
13
|
+
from soothe_client import (
|
|
14
14
|
WebSocketClient,
|
|
15
15
|
request_daemon_config_reload,
|
|
16
16
|
websocket_url_from_config,
|
|
@@ -21,7 +21,7 @@ import typer
|
|
|
21
21
|
from rich.console import Console
|
|
22
22
|
from rich.panel import Panel
|
|
23
23
|
from rich.table import Table
|
|
24
|
-
from
|
|
24
|
+
from soothe_client import (
|
|
25
25
|
is_daemon_live,
|
|
26
26
|
websocket_url_from_config,
|
|
27
27
|
ws_command_client_from_config,
|
|
@@ -17,12 +17,14 @@ from rich.console import Console
|
|
|
17
17
|
from rich.panel import Panel
|
|
18
18
|
from rich.prompt import Prompt
|
|
19
19
|
from rich.table import Table
|
|
20
|
-
from
|
|
21
|
-
ProtocolError,
|
|
20
|
+
from soothe_client import (
|
|
22
21
|
WebSocketClient,
|
|
23
22
|
is_daemon_live,
|
|
24
23
|
websocket_url_from_config,
|
|
25
24
|
)
|
|
25
|
+
from soothe_sdk.client import (
|
|
26
|
+
ProtocolError,
|
|
27
|
+
)
|
|
26
28
|
|
|
27
29
|
from soothe_cli.runtime import load_config
|
|
28
30
|
|
|
@@ -4,10 +4,9 @@ import logging
|
|
|
4
4
|
import sys
|
|
5
5
|
import time
|
|
6
6
|
from pathlib import Path
|
|
7
|
-
from typing import Any
|
|
8
7
|
|
|
9
8
|
import typer
|
|
10
|
-
from soothe_sdk.
|
|
9
|
+
from soothe_sdk.paths import SOOTHE_HOME
|
|
11
10
|
from soothe_sdk.utils.logging import resolve_cli_log_level
|
|
12
11
|
|
|
13
12
|
from soothe_cli.cli.execution import run_headless, run_tui
|
|
@@ -37,7 +36,8 @@ def run_impl(
|
|
|
37
36
|
autonomous: Enable autonomous iteration mode
|
|
38
37
|
max_iterations: Max iterations for autonomous mode
|
|
39
38
|
tui_with_prompt: When True with a prompt, open the TUI instead of headless.
|
|
40
|
-
mcp_config:
|
|
39
|
+
mcp_config: Deprecated. MCP servers must be configured on the daemon;
|
|
40
|
+
passing this flag only emits a warning.
|
|
41
41
|
"""
|
|
42
42
|
startup_start = time.perf_counter()
|
|
43
43
|
use_headless: bool | None = None # Track execution mode for exit tip
|
|
@@ -45,9 +45,13 @@ def run_impl(
|
|
|
45
45
|
try:
|
|
46
46
|
cfg = load_config()
|
|
47
47
|
|
|
48
|
-
#
|
|
48
|
+
# MCP servers are owned by the daemon config; CLI cannot merge them post-split.
|
|
49
49
|
if mcp_config:
|
|
50
|
-
|
|
50
|
+
typer.echo(
|
|
51
|
+
"--mcp-config is ignored in the CLI; configure MCP servers on the daemon "
|
|
52
|
+
f"(~/.soothe/config). Requested file: {mcp_config}",
|
|
53
|
+
err=True,
|
|
54
|
+
)
|
|
51
55
|
log_level = resolve_cli_log_level(logging_level=cfg.logging_level)
|
|
52
56
|
log_file = Path(SOOTHE_HOME) / "logs" / "cli.log"
|
|
53
57
|
setup_logging(log_level, log_file=log_file)
|
|
@@ -123,60 +127,3 @@ def run_impl(
|
|
|
123
127
|
|
|
124
128
|
typer.echo(f"Error: {format_cli_error(e)}", err=True)
|
|
125
129
|
sys.exit(1)
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
def _merge_mcp_config(cfg: Any, mcp_config_path: str) -> None:
|
|
129
|
-
"""Load MCP server config from a JSON/YAML file and merge into SootheConfig.
|
|
130
|
-
|
|
131
|
-
Args:
|
|
132
|
-
cfg: SootheConfig instance.
|
|
133
|
-
mcp_config_path: Path to MCP config file (JSON or YAML).
|
|
134
|
-
"""
|
|
135
|
-
import json
|
|
136
|
-
|
|
137
|
-
path = Path(mcp_config_path).expanduser().resolve()
|
|
138
|
-
if not path.is_file():
|
|
139
|
-
typer.echo(f"--mcp-config: file not found: {path}", err=True)
|
|
140
|
-
return
|
|
141
|
-
|
|
142
|
-
try:
|
|
143
|
-
raw = path.read_text(encoding="utf-8")
|
|
144
|
-
if path.suffix in (".yml", ".yaml"):
|
|
145
|
-
try:
|
|
146
|
-
import yaml
|
|
147
|
-
|
|
148
|
-
data = yaml.safe_load(raw)
|
|
149
|
-
except ImportError:
|
|
150
|
-
typer.echo("--mcp-config: PyYAML not installed; use JSON format", err=True)
|
|
151
|
-
return
|
|
152
|
-
else:
|
|
153
|
-
data = json.loads(raw)
|
|
154
|
-
except Exception as e:
|
|
155
|
-
typer.echo(f"--mcp-config: failed to parse {path}: {e}", err=True)
|
|
156
|
-
return
|
|
157
|
-
|
|
158
|
-
servers = data.get("mcp_servers") or data.get("servers") or []
|
|
159
|
-
if not isinstance(servers, list):
|
|
160
|
-
typer.echo("--mcp-config: expected 'mcp_servers' or 'servers' list", err=True)
|
|
161
|
-
return
|
|
162
|
-
|
|
163
|
-
from soothe.config.models import MCPServerConfig
|
|
164
|
-
|
|
165
|
-
new_servers: list[MCPServerConfig] = []
|
|
166
|
-
for entry in servers:
|
|
167
|
-
if isinstance(entry, dict):
|
|
168
|
-
try:
|
|
169
|
-
new_servers.append(MCPServerConfig(**entry))
|
|
170
|
-
except Exception as e:
|
|
171
|
-
logger.warning("Skipping invalid MCP server entry: %s", e)
|
|
172
|
-
|
|
173
|
-
if new_servers:
|
|
174
|
-
existing = list(cfg.mcp_servers) if cfg.mcp_servers else []
|
|
175
|
-
existing_names = {s.name for s in existing}
|
|
176
|
-
for s in new_servers:
|
|
177
|
-
if s.name in existing_names:
|
|
178
|
-
logger.warning("--mcp-config: server '%s' already in config, skipping", s.name)
|
|
179
|
-
else:
|
|
180
|
-
existing.append(s)
|
|
181
|
-
cfg.mcp_servers = existing
|
|
182
|
-
logger.info("--mcp-config: merged %d MCP server(s)", len(new_servers))
|
|
@@ -12,13 +12,14 @@ import signal
|
|
|
12
12
|
from typing import Any
|
|
13
13
|
|
|
14
14
|
import typer
|
|
15
|
-
from
|
|
15
|
+
from soothe_client import (
|
|
16
16
|
WebSocketClient,
|
|
17
17
|
async_ws_command_client_from_config,
|
|
18
18
|
bootstrap_loop_session,
|
|
19
19
|
connect_websocket_with_retries,
|
|
20
20
|
websocket_url_from_config,
|
|
21
21
|
)
|
|
22
|
+
from soothe_client.appkit import is_loop_scoped_event, unwrap_next
|
|
22
23
|
|
|
23
24
|
from soothe_cli.cli.execution.daemon_errors import (
|
|
24
25
|
friendly_daemon_execution_error,
|
|
@@ -39,46 +40,6 @@ _HEADLESS_WORKER_LOST_RETRIES = 1
|
|
|
39
40
|
_CANCEL_SEND_TIMEOUT_S = 3.0
|
|
40
41
|
|
|
41
42
|
|
|
42
|
-
def _unwrap_next(event: dict[str, Any] | None) -> dict[str, Any] | None:
|
|
43
|
-
"""Unwrap a protocol-1 ``next`` envelope to its inner streaming frame.
|
|
44
|
-
|
|
45
|
-
Under protocol-1 (RFC-450 §9.3) the daemon wraps free-form streaming
|
|
46
|
-
frames (``event``/``command_response``/card replay) in a
|
|
47
|
-
``{proto, type:"next", payload:{namespace, mode, data}}`` envelope. This
|
|
48
|
-
helper returns the inner ``data`` dict (the legacy frame) so the headless
|
|
49
|
-
loop and EventProcessor can branch on the same fields as before.
|
|
50
|
-
``status``/``error``/``response``/``complete`` are sent raw and pass
|
|
51
|
-
through unchanged.
|
|
52
|
-
"""
|
|
53
|
-
if not isinstance(event, dict):
|
|
54
|
-
return event
|
|
55
|
-
if event.get("type") != "next":
|
|
56
|
-
return event
|
|
57
|
-
payload = event.get("payload")
|
|
58
|
-
if not isinstance(payload, dict):
|
|
59
|
-
return event
|
|
60
|
-
data = payload.get("data")
|
|
61
|
-
return data if isinstance(data, dict) else event
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
def _is_loop_scoped_event(event: dict[str, Any], *, active_loop_id: str) -> bool:
|
|
65
|
-
"""Return whether a daemon frame belongs to the active StrangeLoop session.
|
|
66
|
-
|
|
67
|
-
Unwraps protocol-1 ``next`` envelopes first, then checks ``loop_id`` on the
|
|
68
|
-
inner streaming frame. Non-scoped types (``response``, ``error``,
|
|
69
|
-
``complete``, etc.) are always considered in-scope.
|
|
70
|
-
"""
|
|
71
|
-
event_type = event.get("type", "")
|
|
72
|
-
if event_type == "next":
|
|
73
|
-
inner = _unwrap_next(event)
|
|
74
|
-
if isinstance(inner, dict):
|
|
75
|
-
event_type = inner.get("type", "")
|
|
76
|
-
return event_type not in {"status", "event"} or (inner.get("loop_id") == active_loop_id)
|
|
77
|
-
if event_type not in {"status", "event"}:
|
|
78
|
-
return True
|
|
79
|
-
return event.get("loop_id") == active_loop_id
|
|
80
|
-
|
|
81
|
-
|
|
82
43
|
def _emit_headless_error(message: str) -> None:
|
|
83
44
|
"""Write a user-facing error line to stderr (headless renderer convention)."""
|
|
84
45
|
typer.echo(f"ERROR: {message}", err=True)
|
|
@@ -120,8 +81,6 @@ async def _run_headless_session_once(
|
|
|
120
81
|
max_iterations: int | None = None,
|
|
121
82
|
) -> tuple[int, bool]:
|
|
122
83
|
"""Run one headless daemon session; return ``(exit_code, retry_on_worker_loss)``."""
|
|
123
|
-
from soothe_sdk.client import WebSocketClient
|
|
124
|
-
|
|
125
84
|
ws_url = websocket_url_from_config(cfg)
|
|
126
85
|
client = WebSocketClient(url=ws_url)
|
|
127
86
|
|
|
@@ -248,13 +207,13 @@ async def _run_headless_session_once(
|
|
|
248
207
|
break
|
|
249
208
|
|
|
250
209
|
event_type = event.get("type", "")
|
|
251
|
-
if not
|
|
210
|
+
if not is_loop_scoped_event(event, active_loop_id=active_loop_id):
|
|
252
211
|
continue
|
|
253
212
|
|
|
254
213
|
# Unwrap protocol-1 ``next`` envelopes to the inner streaming frame
|
|
255
214
|
# (RFC-450 §9.3). ``status``/``error`` arrive raw and pass through.
|
|
256
215
|
if event_type == "next":
|
|
257
|
-
inner =
|
|
216
|
+
inner = unwrap_next(event)
|
|
258
217
|
if isinstance(inner, dict):
|
|
259
218
|
event = inner
|
|
260
219
|
event_type = event.get("type", "")
|
|
@@ -288,12 +247,12 @@ async def _run_headless_session_once(
|
|
|
288
247
|
break
|
|
289
248
|
if not nxt:
|
|
290
249
|
break
|
|
291
|
-
if not
|
|
250
|
+
if not is_loop_scoped_event(nxt, active_loop_id=active_loop_id):
|
|
292
251
|
continue
|
|
293
252
|
# Unwrap ``next`` envelopes before handing to the
|
|
294
253
|
# processor so it sees the legacy frame shape.
|
|
295
254
|
if nxt.get("type") == "next":
|
|
296
|
-
inner =
|
|
255
|
+
inner = unwrap_next(nxt)
|
|
297
256
|
if isinstance(inner, dict):
|
|
298
257
|
nxt = inner
|
|
299
258
|
processor.process_event(nxt)
|
|
@@ -10,7 +10,7 @@ from pathlib import Path # noqa: E402
|
|
|
10
10
|
from typing import Annotated # noqa: E402
|
|
11
11
|
|
|
12
12
|
import typer # noqa: E402
|
|
13
|
-
from soothe_sdk.
|
|
13
|
+
from soothe_sdk.paths import SOOTHE_HOME # noqa: E402
|
|
14
14
|
|
|
15
15
|
from soothe_cli.config.cli_config import CLIConfig # noqa: E402
|
|
16
16
|
from soothe_cli.config.loader import set_runtime_config # noqa: E402
|
|
@@ -35,7 +35,7 @@ from soothe_cli.runtime.policy.tui_trace_log import log_tui_trace
|
|
|
35
35
|
from soothe_cli.runtime.presentation.engine import PresentationEngine
|
|
36
36
|
|
|
37
37
|
if TYPE_CHECKING:
|
|
38
|
-
from
|
|
38
|
+
from soothe_client.schemas import Plan
|
|
39
39
|
|
|
40
40
|
from soothe_cli.runtime.presentation.renderer_protocol import RendererProtocol
|
|
41
41
|
|
|
@@ -962,7 +962,7 @@ class EventProcessor:
|
|
|
962
962
|
|
|
963
963
|
def _handle_plan_created(self, data: dict[str, Any]) -> None:
|
|
964
964
|
"""Handle plan creation event."""
|
|
965
|
-
from
|
|
965
|
+
from soothe_client.schemas import Plan, PlanStep
|
|
966
966
|
|
|
967
967
|
steps = [
|
|
968
968
|
PlanStep(
|
|
@@ -11,7 +11,7 @@ from dataclasses import dataclass, field
|
|
|
11
11
|
from typing import TYPE_CHECKING, Any
|
|
12
12
|
|
|
13
13
|
if TYPE_CHECKING:
|
|
14
|
-
from
|
|
14
|
+
from soothe_client.schemas import Plan
|
|
15
15
|
|
|
16
16
|
from soothe_cli.runtime.state.stream_accumulator import StreamingTextAccumulator
|
|
17
17
|
|
|
@@ -5,9 +5,11 @@ from __future__ import annotations
|
|
|
5
5
|
from soothe_sdk.display.tool_result import (
|
|
6
6
|
extract_tool_result_payload,
|
|
7
7
|
infer_tool_output_suggests_error,
|
|
8
|
+
is_error_tool_result_text,
|
|
8
9
|
)
|
|
9
10
|
|
|
10
11
|
__all__ = [
|
|
11
12
|
"extract_tool_result_payload",
|
|
12
13
|
"infer_tool_output_suggests_error",
|
|
14
|
+
"is_error_tool_result_text",
|
|
13
15
|
]
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""Daemon-backed session helpers for the Textual TUI.
|
|
2
|
+
|
|
3
|
+
Thin CLI wrapper over ``soothe_client.appkit.DaemonSession`` that derives
|
|
4
|
+
WebSocket URL / stream delivery from CLI config and uses the richer CLI early
|
|
5
|
+
chunk filter (LangChain-aware).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from soothe_client.appkit import DaemonSession
|
|
13
|
+
from soothe_client.helpers import websocket_url_from_config
|
|
14
|
+
|
|
15
|
+
# Re-exported for test monkeypatches / historical import sites.
|
|
16
|
+
from soothe_client.session import ( # noqa: F401
|
|
17
|
+
bootstrap_loop_session,
|
|
18
|
+
connect_websocket_with_retries,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
from soothe_cli.runtime.state.session_stats import TurnEventStats
|
|
22
|
+
from soothe_cli.runtime.wire.chunk_filter import should_drop_stream_chunk_early
|
|
23
|
+
|
|
24
|
+
# Match headless daemon client post-idle drain window.
|
|
25
|
+
_POST_IDLE_DRAIN_DEADLINE_S = 0.5
|
|
26
|
+
|
|
27
|
+
# Brief close handshake on TUI exit — the daemon cleans up on disconnect anyway.
|
|
28
|
+
TUI_EXIT_HANDSHAKE_TIMEOUT_S = 0.3
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class TuiDaemonSession(DaemonSession):
|
|
32
|
+
"""Own the daemon websocket session used by the TUI."""
|
|
33
|
+
|
|
34
|
+
def __init__(
|
|
35
|
+
self,
|
|
36
|
+
cfg: Any,
|
|
37
|
+
*,
|
|
38
|
+
workspace: str | None = None,
|
|
39
|
+
post_idle_drain_deadline: float = _POST_IDLE_DRAIN_DEADLINE_S,
|
|
40
|
+
) -> None:
|
|
41
|
+
self._cfg = cfg
|
|
42
|
+
super().__init__(
|
|
43
|
+
websocket_url_from_config(cfg),
|
|
44
|
+
workspace=workspace,
|
|
45
|
+
stream_delivery=self._resolve_stream_delivery_mode,
|
|
46
|
+
post_idle_drain_deadline=post_idle_drain_deadline,
|
|
47
|
+
early_drop_fn=should_drop_stream_chunk_early,
|
|
48
|
+
stats_factory=TurnEventStats,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
def _resolve_stream_delivery_mode(self) -> str:
|
|
52
|
+
"""Determine stream delivery mode from config (RFC-614).
|
|
53
|
+
|
|
54
|
+
Returns one of ``batch`` | ``adaptive`` | ``streaming``. CLI override
|
|
55
|
+
wins, then config; defaults to ``adaptive``.
|
|
56
|
+
"""
|
|
57
|
+
if (
|
|
58
|
+
self._cfg
|
|
59
|
+
and hasattr(self._cfg, "output_streaming_mode")
|
|
60
|
+
and self._cfg.output_streaming_mode
|
|
61
|
+
):
|
|
62
|
+
return str(self._cfg.output_streaming_mode)
|
|
63
|
+
|
|
64
|
+
if self._cfg and hasattr(self._cfg, "agent"):
|
|
65
|
+
streaming_cfg = self._cfg.agent.loop.output_streaming
|
|
66
|
+
return str(streaming_cfg.mode)
|
|
67
|
+
|
|
68
|
+
return "adaptive"
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
# Historical alias used by some call sites / docs.
|
|
72
|
+
DaemonSession = TuiDaemonSession # noqa: F811
|
|
73
|
+
|
|
74
|
+
__all__ = [
|
|
75
|
+
"DaemonSession",
|
|
76
|
+
"TUI_EXIT_HANDSHAKE_TIMEOUT_S",
|
|
77
|
+
"TuiDaemonSession",
|
|
78
|
+
"bootstrap_loop_session",
|
|
79
|
+
"connect_websocket_with_retries",
|
|
80
|
+
]
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""Decouple daemon stream ingestion from chunk processing and UI application.
|
|
2
|
+
|
|
3
|
+
Canonical implementation lives in ``soothe_client.appkit.turn`` (RFC-629 Layer 1).
|
|
4
|
+
This module re-exports the public API for CLI import stability.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from soothe_client.appkit.turn import (
|
|
10
|
+
_DEFAULT_BATCH_DELAY_MS,
|
|
11
|
+
_DEFAULT_BATCH_SIZE,
|
|
12
|
+
_DEFAULT_INBOUND_MAXSIZE,
|
|
13
|
+
_DEFAULT_OUTBOUND_MAXSIZE,
|
|
14
|
+
PRIORITY_CRITICAL,
|
|
15
|
+
PRIORITY_HIGH,
|
|
16
|
+
PRIORITY_LOW,
|
|
17
|
+
PRIORITY_NORMAL,
|
|
18
|
+
TurnApplyBatcher,
|
|
19
|
+
TurnEventPipeline,
|
|
20
|
+
run_turn_pipeline,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"PRIORITY_CRITICAL",
|
|
25
|
+
"PRIORITY_HIGH",
|
|
26
|
+
"PRIORITY_LOW",
|
|
27
|
+
"PRIORITY_NORMAL",
|
|
28
|
+
"TurnApplyBatcher",
|
|
29
|
+
"TurnEventPipeline",
|
|
30
|
+
"run_turn_pipeline",
|
|
31
|
+
"_DEFAULT_BATCH_SIZE",
|
|
32
|
+
"_DEFAULT_BATCH_DELAY_MS",
|
|
33
|
+
"_DEFAULT_INBOUND_MAXSIZE",
|
|
34
|
+
"_DEFAULT_OUTBOUND_MAXSIZE",
|
|
35
|
+
]
|
|
@@ -287,6 +287,7 @@ class _ModelMixin:
|
|
|
287
287
|
initial_snapshot = await self._load_token_usage_snapshot()
|
|
288
288
|
screen = ContextViewerScreen(
|
|
289
289
|
loop_id=loop_id,
|
|
290
|
+
daemon_session=self._daemon_session,
|
|
290
291
|
load_token_snapshot=self._load_token_usage_snapshot,
|
|
291
292
|
initial_token_snapshot=initial_snapshot,
|
|
292
293
|
)
|
|
@@ -308,7 +309,7 @@ class _ModelMixin:
|
|
|
308
309
|
Args:
|
|
309
310
|
task: Task description for autonomous execution.
|
|
310
311
|
"""
|
|
311
|
-
from
|
|
312
|
+
from soothe_client import (
|
|
312
313
|
async_ws_command_client_from_config,
|
|
313
314
|
is_daemon_live,
|
|
314
315
|
websocket_url_from_config,
|
|
@@ -356,7 +357,7 @@ class _ModelMixin:
|
|
|
356
357
|
text: Natural language schedule and task description.
|
|
357
358
|
slash_input: Original slash command for chat display.
|
|
358
359
|
"""
|
|
359
|
-
from
|
|
360
|
+
from soothe_client import (
|
|
360
361
|
async_ws_command_client_from_config,
|
|
361
362
|
is_daemon_live,
|
|
362
363
|
websocket_url_from_config,
|
|
@@ -473,7 +473,7 @@ class _StartupMixin:
|
|
|
473
473
|
ConnectionError: If the daemon is not reachable or ready in time.
|
|
474
474
|
Exception: If loop bootstrap fails.
|
|
475
475
|
"""
|
|
476
|
-
from
|
|
476
|
+
from soothe_client import (
|
|
477
477
|
is_daemon_live,
|
|
478
478
|
websocket_url_from_config,
|
|
479
479
|
)
|
|
@@ -648,7 +648,7 @@ class _StartupMixin:
|
|
|
648
648
|
# TUI from rendering.
|
|
649
649
|
from langchain_core.messages import AIMessage # noqa: F401
|
|
650
650
|
from langgraph.types import Command # noqa: F401
|
|
651
|
-
from soothe_sdk.
|
|
651
|
+
from soothe_sdk.paths import DEFAULT_EXECUTE_TIMEOUT # noqa: F401
|
|
652
652
|
except Exception:
|
|
653
653
|
logger.warning("Could not prewarm third-party imports", exc_info=True)
|
|
654
654
|
|
|
@@ -16,7 +16,7 @@ from soothe_cli.tui.commands.subagent_routing import parse_subagent_from_input
|
|
|
16
16
|
|
|
17
17
|
if TYPE_CHECKING:
|
|
18
18
|
from rich.console import Console
|
|
19
|
-
from
|
|
19
|
+
from soothe_client import WebSocketClient
|
|
20
20
|
|
|
21
21
|
logger = logging.getLogger(__name__)
|
|
22
22
|
|
|
@@ -13,7 +13,7 @@ from dataclasses import dataclass
|
|
|
13
13
|
from pathlib import Path
|
|
14
14
|
from typing import Any
|
|
15
15
|
|
|
16
|
-
from soothe_sdk.
|
|
16
|
+
from soothe_sdk.paths import SOOTHE_HOME
|
|
17
17
|
|
|
18
18
|
logger = logging.getLogger(__name__)
|
|
19
19
|
|
|
@@ -72,7 +72,10 @@ async def _fetch_provider_config(provider_name: str) -> dict[str, Any] | None:
|
|
|
72
72
|
Provider config dict or None if not found.
|
|
73
73
|
"""
|
|
74
74
|
try:
|
|
75
|
-
from
|
|
75
|
+
from soothe_client import (
|
|
76
|
+
WebSocketClient,
|
|
77
|
+
fetch_config_section,
|
|
78
|
+
)
|
|
76
79
|
|
|
77
80
|
from soothe_cli.config.loader import load_config
|
|
78
81
|
|
|
@@ -480,7 +483,10 @@ def get_available_models() -> list[ModelProfileEntry]:
|
|
|
480
483
|
try:
|
|
481
484
|
import asyncio
|
|
482
485
|
|
|
483
|
-
from
|
|
486
|
+
from soothe_client import (
|
|
487
|
+
WebSocketClient,
|
|
488
|
+
fetch_config_section,
|
|
489
|
+
)
|
|
484
490
|
|
|
485
491
|
# Use CLIConfig to get WebSocket URL
|
|
486
492
|
from soothe_cli.config.loader import load_config
|
|
@@ -6,7 +6,10 @@ import logging
|
|
|
6
6
|
from collections import OrderedDict
|
|
7
7
|
from typing import TYPE_CHECKING
|
|
8
8
|
|
|
9
|
-
from
|
|
9
|
+
from soothe_client import (
|
|
10
|
+
fetch_skills_catalog,
|
|
11
|
+
websocket_url_from_config,
|
|
12
|
+
)
|
|
10
13
|
|
|
11
14
|
from soothe_cli.tui.skills.load import ExtendedSkillMetadata
|
|
12
15
|
|
|
@@ -33,7 +36,7 @@ async def discover_skills_async(
|
|
|
33
36
|
(built-in first, winning entry last). Empty list if daemon
|
|
34
37
|
unavailable.
|
|
35
38
|
"""
|
|
36
|
-
from
|
|
39
|
+
from soothe_client import WebSocketClient
|
|
37
40
|
|
|
38
41
|
if daemon_config is None:
|
|
39
42
|
logger.warning("No daemon_config provided for skills discovery; returning empty catalog")
|
|
@@ -29,7 +29,7 @@ from pathlib import Path
|
|
|
29
29
|
from types import MappingProxyType
|
|
30
30
|
from typing import TYPE_CHECKING, Any, ClassVar
|
|
31
31
|
|
|
32
|
-
from soothe_sdk.
|
|
32
|
+
from soothe_sdk.paths import SOOTHE_HOME
|
|
33
33
|
|
|
34
34
|
if TYPE_CHECKING:
|
|
35
35
|
from collections.abc import Mapping
|
|
@@ -5,10 +5,10 @@ from __future__ import annotations
|
|
|
5
5
|
import json
|
|
6
6
|
from typing import Any
|
|
7
7
|
|
|
8
|
-
from soothe_sdk.client.protocol import preview_first
|
|
9
8
|
from soothe_sdk.tools.metadata import get_tool_meta
|
|
10
9
|
from soothe_sdk.utils import get_tool_display_name
|
|
11
10
|
from soothe_sdk.utils.formatting import convert_and_abbreviate_path
|
|
11
|
+
from soothe_sdk.wire.protocol import preview_first
|
|
12
12
|
|
|
13
13
|
from soothe_cli.runtime.parse.message_processing import (
|
|
14
14
|
_normalize_tool_name_for_arg_map,
|
|
@@ -21,7 +21,7 @@ from pathlib import Path
|
|
|
21
21
|
from typing import Literal
|
|
22
22
|
|
|
23
23
|
from packaging.version import InvalidVersion, Version
|
|
24
|
-
from soothe_sdk.
|
|
24
|
+
from soothe_sdk.paths import SOOTHE_HOME
|
|
25
25
|
|
|
26
26
|
from soothe_cli.tui._version import PYPI_URL, USER_AGENT, __version__
|
|
27
27
|
|
|
@@ -9,7 +9,7 @@ import time
|
|
|
9
9
|
from pathlib import Path
|
|
10
10
|
from typing import TYPE_CHECKING, Any, ClassVar
|
|
11
11
|
|
|
12
|
-
from soothe_sdk.
|
|
12
|
+
from soothe_sdk.paths import SOOTHE_DATA_DIR
|
|
13
13
|
from textual.binding import Binding
|
|
14
14
|
from textual.containers import Horizontal, Vertical, VerticalScroll
|
|
15
15
|
from textual.content import Content
|