soothe-cli 0.1.0__tar.gz → 0.3.3__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.1.0 → soothe_cli-0.3.3}/.gitignore +1 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/PKG-INFO +3 -3
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/pyproject.toml +4 -4
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/cli/commands/autopilot_cmd.py +11 -12
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/cli/commands/config_cmd.py +3 -3
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/cli/commands/run_cmd.py +7 -11
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/cli/commands/status_cmd.py +2 -2
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/cli/commands/thread_cmd.py +7 -3
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/cli/execution/daemon.py +2 -2
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/cli/execution/launcher.py +0 -1
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/cli/main.py +7 -12
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/cli/renderer.py +6 -8
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/cli/stream/display_line.py +8 -5
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/cli/stream/formatter.py +78 -18
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/cli/stream/pipeline.py +162 -41
- soothe_cli-0.3.3/src/soothe_cli/config/__init__.py +5 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/config/cli_config.py +29 -5
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/shared/__init__.py +3 -1
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/shared/command_router.py +9 -3
- soothe_cli-0.3.3/src/soothe_cli/shared/config_loader.py +64 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/shared/display_policy.py +22 -7
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/shared/event_processor.py +35 -33
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/shared/message_processing.py +90 -30
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/shared/presentation_engine.py +1 -1
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/shared/processor_state.py +1 -1
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/shared/renderer_protocol.py +1 -1
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/shared/rendering.py +1 -1
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/shared/suppression_state.py +5 -4
- soothe_cli-0.3.3/src/soothe_cli/shared/tool_call_resolution.py +279 -0
- soothe_cli-0.3.3/src/soothe_cli/shared/tool_card_payload.py +121 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/shared/tool_formatters/fallback.py +2 -2
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/shared/tool_formatters/structured.py +3 -3
- soothe_cli-0.3.3/src/soothe_cli/shared/tool_message_format.py +77 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/shared/tool_output_formatter.py +1 -1
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/shared/tui_trace_log.py +1 -1
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/app.py +66 -11
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/command_registry.py +21 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/config.py +1 -1
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/daemon_session.py +45 -1
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/model_config.py +1 -1
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/sessions.py +1 -1
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/textual_adapter.py +601 -80
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/theme.py +29 -2
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/tool_display.py +111 -100
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/update_check.py +1 -1
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/widgets/autopilot_dashboard.py +4 -4
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/widgets/autopilot_screen.py +1 -1
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/widgets/chat_input.py +1 -1
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/widgets/history.py +1 -1
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/widgets/message_store.py +150 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/widgets/messages.py +674 -17
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/widgets/thread_selector.py +3 -67
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/widgets/welcome.py +5 -75
- soothe_cli-0.1.0/src/soothe_cli/config/__init__.py +0 -5
- soothe_cli-0.1.0/src/soothe_cli/shared/config_loader.py +0 -68
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/README.md +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/__init__.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/cli/__init__.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/cli/commands/__init__.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/cli/commands/subagent_names.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/cli/execution/__init__.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/cli/execution/headless.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/cli/stream/__init__.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/cli/stream/context.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/cli/utils.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/plan/__init__.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/plan/rich_tree.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/shared/essential_events.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/shared/slash_commands.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/shared/subagent_routing.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/shared/tool_formatters/__init__.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/shared/tool_formatters/base.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/shared/tool_formatters/execution.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/shared/tool_formatters/file_ops.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/shared/tool_formatters/goal_formatter.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/shared/tool_formatters/media.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/shared/tool_formatters/web.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/__init__.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/_ask_user_types.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/_cli_context.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/_env_vars.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/_session_stats.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/_version.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/app.tcss +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/file_ops.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/formatting.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/hooks.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/input.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/media_utils.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/output.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/project_utils.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/skills/__init__.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/skills/invocation.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/skills/load.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/unicode_security.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/widgets/__init__.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/widgets/_links.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/widgets/approval.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/widgets/ask_user.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/widgets/autocomplete.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/widgets/clipboard.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/widgets/diff.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/widgets/editor.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/widgets/loading.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/widgets/mcp_viewer.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/widgets/model_selector.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/widgets/notification_settings.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/widgets/status.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/widgets/theme_selector.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/widgets/tool_renderers.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/widgets/tool_widgets.py +0 -0
- {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/widgets/tools.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: soothe-cli
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary: Soothe CLI client - communicates with daemon via WebSocket
|
|
3
|
+
Version: 0.3.3
|
|
4
|
+
Summary: Soothe CLI client - communicates with daemon via WebSocket (updated for SDK v0.4.0)
|
|
5
5
|
Project-URL: Homepage, https://github.com/caesar0301/soothe
|
|
6
6
|
Project-URL: Documentation, https://soothe.readthedocs.io
|
|
7
7
|
Project-URL: Repository, https://github.com/caesar0301/soothe
|
|
@@ -21,7 +21,7 @@ Requires-Python: <3.15,>=3.11
|
|
|
21
21
|
Requires-Dist: python-dotenv<2.0.0,>=1.0.0
|
|
22
22
|
Requires-Dist: pyyaml<7.0.0,>=6.0.0
|
|
23
23
|
Requires-Dist: rich>=13.0.0
|
|
24
|
-
Requires-Dist: soothe-sdk<1.0.0,>=0.
|
|
24
|
+
Requires-Dist: soothe-sdk<1.0.0,>=0.4.0
|
|
25
25
|
Requires-Dist: textual>=0.40.0
|
|
26
26
|
Requires-Dist: typer<1.0.0,>=0.9.0
|
|
27
27
|
Requires-Dist: websockets>=12.0
|
|
@@ -4,8 +4,8 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "soothe-cli"
|
|
7
|
-
version = "0.
|
|
8
|
-
description = "Soothe CLI client - communicates with daemon via WebSocket"
|
|
7
|
+
version = "0.3.3"
|
|
8
|
+
description = "Soothe CLI client - communicates with daemon via WebSocket (updated for SDK v0.4.0)"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
|
11
11
|
requires-python = ">=3.11,<3.15"
|
|
@@ -23,7 +23,7 @@ classifiers = [
|
|
|
23
23
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
24
24
|
]
|
|
25
25
|
dependencies = [
|
|
26
|
-
"soothe-sdk>=0.
|
|
26
|
+
"soothe-sdk>=0.4.0,<1.0.0", # WebSocket client, protocol, types (v0.4.0 breaking change)
|
|
27
27
|
"typer>=0.9.0,<1.0.0", # CLI framework
|
|
28
28
|
"textual>=0.40.0", # TUI framework
|
|
29
29
|
"rich>=13.0.0", # Console output
|
|
@@ -75,4 +75,4 @@ docstring-code-format = true
|
|
|
75
75
|
python_version = "3.11"
|
|
76
76
|
warn_return_any = true
|
|
77
77
|
warn_unused_configs = true
|
|
78
|
-
disallow_untyped_defs = true
|
|
78
|
+
disallow_untyped_defs = true
|
|
@@ -9,7 +9,7 @@ from __future__ import annotations
|
|
|
9
9
|
from pathlib import Path
|
|
10
10
|
|
|
11
11
|
import typer
|
|
12
|
-
from soothe_sdk.protocol import preview_first
|
|
12
|
+
from soothe_sdk.client.protocol import preview_first
|
|
13
13
|
|
|
14
14
|
app = typer.Typer(help="Autopilot mode — long-running autonomous agent control.")
|
|
15
15
|
|
|
@@ -40,7 +40,6 @@ def run(
|
|
|
40
40
|
autonomous=True,
|
|
41
41
|
max_iterations=max_iterations,
|
|
42
42
|
output_format=output_format,
|
|
43
|
-
verbosity=None,
|
|
44
43
|
)
|
|
45
44
|
|
|
46
45
|
|
|
@@ -55,7 +54,7 @@ def submit(
|
|
|
55
54
|
"""
|
|
56
55
|
from datetime import UTC, datetime
|
|
57
56
|
|
|
58
|
-
from soothe_sdk import SOOTHE_HOME
|
|
57
|
+
from soothe_sdk.client.config import SOOTHE_HOME
|
|
59
58
|
|
|
60
59
|
inbox_dir = SOOTHE_HOME / "autopilot" / "inbox"
|
|
61
60
|
inbox_dir.mkdir(parents=True, exist_ok=True)
|
|
@@ -72,7 +71,7 @@ def submit(
|
|
|
72
71
|
@app.command("status")
|
|
73
72
|
def status() -> None:
|
|
74
73
|
"""Show overall autopilot state."""
|
|
75
|
-
from soothe_sdk import SOOTHE_HOME
|
|
74
|
+
from soothe_sdk.client.config import SOOTHE_HOME
|
|
76
75
|
|
|
77
76
|
autopilot_dir = SOOTHE_HOME / "autopilot"
|
|
78
77
|
state_file = autopilot_dir / "status.json"
|
|
@@ -106,7 +105,7 @@ def list_goals(
|
|
|
106
105
|
status_filter: str = typer.Option("", "--status", "-s", help="Filter by status."),
|
|
107
106
|
) -> None:
|
|
108
107
|
"""List all goals."""
|
|
109
|
-
from soothe_sdk import SOOTHE_HOME
|
|
108
|
+
from soothe_sdk.client.config import SOOTHE_HOME
|
|
110
109
|
|
|
111
110
|
autopilot_dir = SOOTHE_HOME / "autopilot"
|
|
112
111
|
goals = _discover_goals(autopilot_dir)
|
|
@@ -130,7 +129,7 @@ def show_goal(
|
|
|
130
129
|
goal_id: str = typer.Argument(..., help="Goal ID to show details for."),
|
|
131
130
|
) -> None:
|
|
132
131
|
"""Show details for a specific goal."""
|
|
133
|
-
from soothe_sdk import SOOTHE_HOME
|
|
132
|
+
from soothe_sdk.client.config import SOOTHE_HOME
|
|
134
133
|
|
|
135
134
|
autopilot_dir = SOOTHE_HOME / "autopilot"
|
|
136
135
|
goals = _discover_goals(autopilot_dir)
|
|
@@ -160,7 +159,7 @@ def cancel_goal(
|
|
|
160
159
|
goal_id: str = typer.Argument(..., help="Goal ID to cancel."),
|
|
161
160
|
) -> None:
|
|
162
161
|
"""Cancel a goal (remove from inbox if pending)."""
|
|
163
|
-
from soothe_sdk import SOOTHE_HOME
|
|
162
|
+
from soothe_sdk.client.config import SOOTHE_HOME
|
|
164
163
|
|
|
165
164
|
inbox_dir = SOOTHE_HOME / "autopilot" / "inbox"
|
|
166
165
|
if not inbox_dir.exists():
|
|
@@ -190,7 +189,7 @@ def approve_goal(
|
|
|
190
189
|
"""Approve a MUST-confirmation goal."""
|
|
191
190
|
import json
|
|
192
191
|
|
|
193
|
-
from soothe_sdk import SOOTHE_HOME
|
|
192
|
+
from soothe_sdk.client.config import SOOTHE_HOME
|
|
194
193
|
|
|
195
194
|
confirmations_file = SOOTHE_HOME / "autopilot" / "pending_confirmations.json"
|
|
196
195
|
if not confirmations_file.exists():
|
|
@@ -223,7 +222,7 @@ def reject_goal(
|
|
|
223
222
|
"""Reject a proposed goal."""
|
|
224
223
|
import json
|
|
225
224
|
|
|
226
|
-
from soothe_sdk import SOOTHE_HOME
|
|
225
|
+
from soothe_sdk.client.config import SOOTHE_HOME
|
|
227
226
|
|
|
228
227
|
confirmations_file = SOOTHE_HOME / "autopilot" / "pending_confirmations.json"
|
|
229
228
|
if not confirmations_file.exists():
|
|
@@ -248,7 +247,7 @@ def reject_goal(
|
|
|
248
247
|
@app.command("wake")
|
|
249
248
|
def wake() -> None:
|
|
250
249
|
"""Exit dreaming mode — resume active execution."""
|
|
251
|
-
from soothe_sdk import SOOTHE_HOME
|
|
250
|
+
from soothe_sdk.client.config import SOOTHE_HOME
|
|
252
251
|
|
|
253
252
|
inbox_dir = SOOTHE_HOME / "autopilot" / "inbox"
|
|
254
253
|
inbox_dir.mkdir(parents=True, exist_ok=True)
|
|
@@ -261,7 +260,7 @@ def wake() -> None:
|
|
|
261
260
|
@app.command("dream")
|
|
262
261
|
def dream() -> None:
|
|
263
262
|
"""Force enter dreaming mode."""
|
|
264
|
-
from soothe_sdk import SOOTHE_HOME
|
|
263
|
+
from soothe_sdk.client.config import SOOTHE_HOME
|
|
265
264
|
|
|
266
265
|
inbox_dir = SOOTHE_HOME / "autopilot" / "inbox"
|
|
267
266
|
inbox_dir.mkdir(parents=True, exist_ok=True)
|
|
@@ -276,7 +275,7 @@ def view_inbox(
|
|
|
276
275
|
limit: int = typer.Option(10, "--limit", "-n", help="Max tasks to show."),
|
|
277
276
|
) -> None:
|
|
278
277
|
"""View pending inbox tasks."""
|
|
279
|
-
from soothe_sdk import SOOTHE_HOME
|
|
278
|
+
from soothe_sdk.client.config import SOOTHE_HOME
|
|
280
279
|
|
|
281
280
|
inbox_dir = SOOTHE_HOME / "autopilot" / "inbox"
|
|
282
281
|
if not inbox_dir.exists():
|
|
@@ -113,7 +113,7 @@ def config_show(
|
|
|
113
113
|
sys.exit(0)
|
|
114
114
|
except Exception as e:
|
|
115
115
|
logger.exception("Config command error")
|
|
116
|
-
from soothe_sdk import format_cli_error
|
|
116
|
+
from soothe_sdk.utils import format_cli_error
|
|
117
117
|
|
|
118
118
|
typer.echo(f"Error: {format_cli_error(e)}", err=True)
|
|
119
119
|
sys.exit(1)
|
|
@@ -137,7 +137,7 @@ def config_init(
|
|
|
137
137
|
from importlib.resources import as_file, files
|
|
138
138
|
from pathlib import Path
|
|
139
139
|
|
|
140
|
-
from soothe_sdk import SOOTHE_HOME
|
|
140
|
+
from soothe_sdk.client.config import SOOTHE_HOME
|
|
141
141
|
|
|
142
142
|
home = Path(SOOTHE_HOME).expanduser()
|
|
143
143
|
target = home / "config" / "config.yml"
|
|
@@ -209,7 +209,7 @@ def config_validate(
|
|
|
209
209
|
"""
|
|
210
210
|
from pathlib import Path
|
|
211
211
|
|
|
212
|
-
from soothe_sdk import SOOTHE_HOME
|
|
212
|
+
from soothe_sdk.client.config import SOOTHE_HOME
|
|
213
213
|
|
|
214
214
|
try:
|
|
215
215
|
cfg = load_config(config)
|
|
@@ -4,10 +4,10 @@ import logging
|
|
|
4
4
|
import sys
|
|
5
5
|
import time
|
|
6
6
|
from pathlib import Path
|
|
7
|
-
from typing import Literal
|
|
8
7
|
|
|
9
8
|
import typer
|
|
10
|
-
from soothe_sdk import SOOTHE_HOME
|
|
9
|
+
from soothe_sdk.client.config import SOOTHE_HOME
|
|
10
|
+
from soothe_sdk.utils.logging import resolve_cli_log_level
|
|
11
11
|
|
|
12
12
|
from soothe_cli.cli.execution import run_headless, run_tui
|
|
13
13
|
from soothe_cli.shared import load_config, setup_logging
|
|
@@ -23,28 +23,24 @@ def run_impl(
|
|
|
23
23
|
autonomous: bool, # noqa: FBT001
|
|
24
24
|
max_iterations: int | None,
|
|
25
25
|
output_format: str,
|
|
26
|
-
verbosity: Literal["quiet", "minimal", "normal", "detailed", "debug"] | None,
|
|
27
26
|
) -> None:
|
|
28
27
|
"""Core implementation for running Soothe agent.
|
|
29
28
|
|
|
30
29
|
Args:
|
|
31
30
|
prompt: Optional prompt for headless mode
|
|
32
|
-
config:
|
|
31
|
+
config: Deprecated; passed through for ``--config`` compatibility (ignored for
|
|
32
|
+
client settings; see ``load_config``).
|
|
33
33
|
thread_id: Thread ID to resume
|
|
34
34
|
no_tui: Force headless mode
|
|
35
35
|
autonomous: Enable autonomous iteration mode
|
|
36
36
|
max_iterations: Max iterations for autonomous mode
|
|
37
37
|
output_format: Output format (text or jsonl)
|
|
38
|
-
verbosity: Verbosity level
|
|
39
38
|
"""
|
|
40
39
|
startup_start = time.perf_counter()
|
|
41
40
|
|
|
42
41
|
try:
|
|
43
42
|
cfg = load_config(config)
|
|
44
|
-
|
|
45
|
-
logging_config = cfg.logging.model_copy(update={"verbosity": verbosity})
|
|
46
|
-
cfg = cfg.model_copy(update={"logging": logging_config})
|
|
47
|
-
log_level = VERBOSITY_TO_LOG_LEVEL.get(cfg.logging.verbosity, "INFO")
|
|
43
|
+
log_level = resolve_cli_log_level(cfg.verbosity, logging_level=cfg.logging_level)
|
|
48
44
|
log_file = Path(SOOTHE_HOME) / "logs" / "soothe-cli.log"
|
|
49
45
|
setup_logging(log_level, log_file=log_file)
|
|
50
46
|
|
|
@@ -71,7 +67,7 @@ def run_impl(
|
|
|
71
67
|
)
|
|
72
68
|
else:
|
|
73
69
|
# TUI mode (with optional initial prompt)
|
|
74
|
-
run_tui(cfg, thread_id=thread_id,
|
|
70
|
+
run_tui(cfg, thread_id=thread_id, initial_prompt=prompt)
|
|
75
71
|
|
|
76
72
|
run_elapsed_s = time.perf_counter() - run_start
|
|
77
73
|
typer.echo(f"Total running time: {run_elapsed_s:.2f}s", err=True)
|
|
@@ -81,7 +77,7 @@ def run_impl(
|
|
|
81
77
|
sys.exit(0)
|
|
82
78
|
except Exception as e:
|
|
83
79
|
logger.exception("CLI run error")
|
|
84
|
-
from soothe_sdk import format_cli_error
|
|
80
|
+
from soothe_sdk.utils import format_cli_error
|
|
85
81
|
|
|
86
82
|
typer.echo(f"Error: {format_cli_error(e)}", err=True)
|
|
87
83
|
sys.exit(1)
|
|
@@ -80,7 +80,7 @@ def agent_list(
|
|
|
80
80
|
sys.exit(0)
|
|
81
81
|
except Exception as e:
|
|
82
82
|
logger.exception("Agent list error")
|
|
83
|
-
from soothe_sdk import format_cli_error
|
|
83
|
+
from soothe_sdk.utils import format_cli_error
|
|
84
84
|
|
|
85
85
|
typer.echo(f"Error: {format_cli_error(e)}", err=True)
|
|
86
86
|
sys.exit(1)
|
|
@@ -115,7 +115,7 @@ def agent_status(
|
|
|
115
115
|
typer.echo(f"\nTotal: {enabled_count}/{total_count} agents enabled")
|
|
116
116
|
except Exception as e:
|
|
117
117
|
logger.exception("Agent status error")
|
|
118
|
-
from soothe_sdk import format_cli_error
|
|
118
|
+
from soothe_sdk.utils import format_cli_error
|
|
119
119
|
|
|
120
120
|
typer.echo(f"Error: {format_cli_error(e)}", err=True)
|
|
121
121
|
sys.exit(1)
|
|
@@ -10,8 +10,9 @@ from pathlib import Path
|
|
|
10
10
|
from typing import Annotated, Any
|
|
11
11
|
|
|
12
12
|
import typer
|
|
13
|
-
from soothe_sdk import SOOTHE_HOME, VERBOSITY_TO_LOG_LEVEL
|
|
14
13
|
from soothe_sdk.client import WebSocketClient, is_daemon_live, websocket_url_from_config
|
|
14
|
+
from soothe_sdk.client.config import SOOTHE_HOME
|
|
15
|
+
from soothe_sdk.utils.logging import resolve_cli_log_level
|
|
15
16
|
|
|
16
17
|
from soothe_cli.shared import load_config
|
|
17
18
|
|
|
@@ -208,7 +209,10 @@ def thread_continue(
|
|
|
208
209
|
from soothe_cli.shared import setup_logging
|
|
209
210
|
|
|
210
211
|
cfg = load_config(config)
|
|
211
|
-
log_level =
|
|
212
|
+
log_level = resolve_cli_log_level(
|
|
213
|
+
cfg.logging.verbosity,
|
|
214
|
+
logging_level=cfg.logging.level,
|
|
215
|
+
)
|
|
212
216
|
log_file = Path(SOOTHE_HOME) / "logs" / "soothe-cli.log"
|
|
213
217
|
setup_logging(log_level, log_file=log_file)
|
|
214
218
|
ws_url = websocket_url_from_config(cfg)
|
|
@@ -245,7 +249,7 @@ def thread_continue(
|
|
|
245
249
|
|
|
246
250
|
thread_id = asyncio.run(get_last_thread_via_daemon())
|
|
247
251
|
|
|
248
|
-
run_tui(cfg, thread_id=thread_id
|
|
252
|
+
run_tui(cfg, thread_id=thread_id)
|
|
249
253
|
|
|
250
254
|
|
|
251
255
|
def thread_archive(
|
|
@@ -178,13 +178,13 @@ async def run_headless_via_daemon(
|
|
|
178
178
|
|
|
179
179
|
except (ConnectionError, OSError, TimeoutError) as e:
|
|
180
180
|
logger.exception("Daemon connection failed")
|
|
181
|
-
from soothe_sdk import format_cli_error
|
|
181
|
+
from soothe_sdk.utils import format_cli_error
|
|
182
182
|
|
|
183
183
|
typer.echo(f"Error: {format_cli_error(e, context='daemon connection')}", err=True)
|
|
184
184
|
return _DAEMON_FALLBACK_EXIT_CODE
|
|
185
185
|
except Exception as e:
|
|
186
186
|
logger.exception("Failed to run via daemon")
|
|
187
|
-
from soothe_sdk import format_cli_error
|
|
187
|
+
from soothe_sdk.utils import format_cli_error
|
|
188
188
|
|
|
189
189
|
typer.echo(f"Error: {format_cli_error(e)}", err=True)
|
|
190
190
|
return 1
|
|
@@ -7,7 +7,7 @@ from dotenv import load_dotenv
|
|
|
7
7
|
load_dotenv()
|
|
8
8
|
|
|
9
9
|
from importlib.metadata import version # noqa: E402
|
|
10
|
-
from typing import Annotated
|
|
10
|
+
from typing import Annotated # noqa: E402
|
|
11
11
|
|
|
12
12
|
import typer # noqa: E402
|
|
13
13
|
|
|
@@ -54,7 +54,11 @@ def main(
|
|
|
54
54
|
ctx: typer.Context,
|
|
55
55
|
config: Annotated[
|
|
56
56
|
str | None,
|
|
57
|
-
typer.Option(
|
|
57
|
+
typer.Option(
|
|
58
|
+
"--config",
|
|
59
|
+
"-c",
|
|
60
|
+
help="Ignored for client settings; edit ~/.soothe/config/cli_config.yml instead.",
|
|
61
|
+
),
|
|
58
62
|
] = None,
|
|
59
63
|
prompt: Annotated[
|
|
60
64
|
str | None,
|
|
@@ -70,14 +74,6 @@ def main(
|
|
|
70
74
|
str,
|
|
71
75
|
typer.Option("--format", "-f", help="Output format for headless mode: text or jsonl."),
|
|
72
76
|
] = "text",
|
|
73
|
-
verbosity: Annotated[
|
|
74
|
-
Literal["quiet", "minimal", "normal", "detailed", "debug"] | None,
|
|
75
|
-
typer.Option(
|
|
76
|
-
"--verbosity",
|
|
77
|
-
"-v",
|
|
78
|
-
help="Verbosity level: quiet, normal, detailed, debug. 'minimal' is accepted as an alias.",
|
|
79
|
-
),
|
|
80
|
-
] = None,
|
|
81
77
|
show_help: Annotated[ # noqa: FBT002
|
|
82
78
|
bool,
|
|
83
79
|
typer.Option("--help", "-h", is_flag=True, help="Show this message and exit."),
|
|
@@ -96,7 +92,7 @@ def main(
|
|
|
96
92
|
Examples:
|
|
97
93
|
soothe # Interactive TUI mode
|
|
98
94
|
soothe -p "Research AI advances" # Headless single-prompt mode
|
|
99
|
-
soothe --config custom.yml #
|
|
95
|
+
soothe --config custom.yml # Ignored for client settings; use ~/.soothe/config/cli_config.yml
|
|
100
96
|
soothe thread list # List conversation threads
|
|
101
97
|
"""
|
|
102
98
|
# Handle -h/--help flag
|
|
@@ -121,7 +117,6 @@ def main(
|
|
|
121
117
|
autonomous=False,
|
|
122
118
|
max_iterations=None,
|
|
123
119
|
output_format=output_format,
|
|
124
|
-
verbosity=verbosity,
|
|
125
120
|
)
|
|
126
121
|
|
|
127
122
|
|
|
@@ -12,7 +12,7 @@ import time
|
|
|
12
12
|
from dataclasses import dataclass, field
|
|
13
13
|
from typing import TYPE_CHECKING, Any
|
|
14
14
|
|
|
15
|
-
from soothe_sdk import get_tool_display_name
|
|
15
|
+
from soothe_sdk.utils import get_tool_display_name
|
|
16
16
|
from soothe_sdk.verbosity import VerbosityTier
|
|
17
17
|
|
|
18
18
|
from soothe_cli.cli.stream import DisplayLine, StreamDisplayPipeline
|
|
@@ -23,7 +23,7 @@ from soothe_cli.shared.presentation_engine import PresentationEngine
|
|
|
23
23
|
from soothe_cli.shared.suppression_state import SuppressionState
|
|
24
24
|
|
|
25
25
|
if TYPE_CHECKING:
|
|
26
|
-
from soothe_sdk import Plan
|
|
26
|
+
from soothe_sdk.client.schemas import Plan
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
@dataclass
|
|
@@ -207,9 +207,9 @@ class CliRenderer:
|
|
|
207
207
|
if not self._presentation.tier_visible(VerbosityTier.NORMAL, self._verbosity):
|
|
208
208
|
return
|
|
209
209
|
|
|
210
|
-
#
|
|
211
|
-
|
|
212
|
-
|
|
210
|
+
# Multi-step / agentic suppression applies to assistant stdout only (IG-143).
|
|
211
|
+
# Tool calls and results still stream to stderr at normal+ verbosity so headless
|
|
212
|
+
# runs show the same tool activity as the TUI.
|
|
213
213
|
|
|
214
214
|
self._stderr_begin_icon_block()
|
|
215
215
|
|
|
@@ -251,9 +251,7 @@ class CliRenderer:
|
|
|
251
251
|
if not self._presentation.tier_visible(VerbosityTier.NORMAL, self._verbosity):
|
|
252
252
|
return
|
|
253
253
|
|
|
254
|
-
#
|
|
255
|
-
if self._state.suppression.should_suppress_output():
|
|
256
|
-
return
|
|
254
|
+
# See on_tool_call: do not suppress stderr tool results during multi-step runs.
|
|
257
255
|
|
|
258
256
|
self._stderr_begin_icon_block()
|
|
259
257
|
|
|
@@ -66,18 +66,21 @@ class DisplayLine:
|
|
|
66
66
|
return "".join(parts)
|
|
67
67
|
|
|
68
68
|
|
|
69
|
-
def indent_for_level(
|
|
69
|
+
def indent_for_level(level: int) -> str:
|
|
70
70
|
"""Get indentation string for a display level.
|
|
71
71
|
|
|
72
|
-
Headless CLI uses
|
|
72
|
+
IG-182: Headless CLI uses flat layout for levels 1-2, but tree indentation
|
|
73
|
+
for level-3 child nodes (step results with "|__" connector).
|
|
73
74
|
|
|
74
75
|
Args:
|
|
75
|
-
|
|
76
|
+
level: Display level (1=goal, 2=step/tool, 3=result child).
|
|
76
77
|
|
|
77
78
|
Returns:
|
|
78
|
-
Indentation string
|
|
79
|
+
Indentation string: "" for level 1-2, " " for level 3 (tree child).
|
|
79
80
|
"""
|
|
80
|
-
|
|
81
|
+
if level >= 3: # noqa: PLR2004
|
|
82
|
+
return " " # 2-space indent for tree children (IG-182)
|
|
83
|
+
return "" # Flat layout for goal/step headers
|
|
81
84
|
|
|
82
85
|
|
|
83
86
|
__all__ = ["DisplayLine", "indent_for_level"]
|
|
@@ -261,6 +261,25 @@ def format_subagent_done(
|
|
|
261
261
|
)
|
|
262
262
|
|
|
263
263
|
|
|
264
|
+
def format_plan_phase_reasoning(
|
|
265
|
+
label: str,
|
|
266
|
+
text: str,
|
|
267
|
+
*,
|
|
268
|
+
namespace: tuple[str, ...] = (),
|
|
269
|
+
verbosity_tier: VerbosityTier = VerbosityTier.NORMAL,
|
|
270
|
+
) -> DisplayLine:
|
|
271
|
+
"""Format a labeled plan-phase reasoning line (assessment vs plan strategy)."""
|
|
272
|
+
content = f"💭 {label}: {text}"
|
|
273
|
+
return DisplayLine(
|
|
274
|
+
level=3,
|
|
275
|
+
content=content,
|
|
276
|
+
icon="•",
|
|
277
|
+
indent=indent_for_level(3),
|
|
278
|
+
source_prefix=_derive_source_prefix(namespace, verbosity_tier),
|
|
279
|
+
newline_before=True,
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
|
|
264
283
|
def format_reasoning(
|
|
265
284
|
reasoning: str,
|
|
266
285
|
*,
|
|
@@ -280,7 +299,7 @@ def format_reasoning(
|
|
|
280
299
|
DisplayLine for reasoning.
|
|
281
300
|
"""
|
|
282
301
|
# Polish: Add "Reasoning:" prefix to make internal analysis visible
|
|
283
|
-
content = f"💭
|
|
302
|
+
content = f"💭 {reasoning}"
|
|
284
303
|
|
|
285
304
|
return DisplayLine(
|
|
286
305
|
level=3, # Use level 3 for less prominence (subordinate to next_action)
|
|
@@ -296,6 +315,7 @@ def format_judgement(
|
|
|
296
315
|
judgement: str,
|
|
297
316
|
action: str,
|
|
298
317
|
*,
|
|
318
|
+
plan_action: str | None = None,
|
|
299
319
|
namespace: tuple[str, ...] = (),
|
|
300
320
|
verbosity_tier: VerbosityTier = VerbosityTier.NORMAL,
|
|
301
321
|
) -> DisplayLine:
|
|
@@ -307,6 +327,7 @@ def format_judgement(
|
|
|
307
327
|
Args:
|
|
308
328
|
judgement: Human-readable summary of the decision.
|
|
309
329
|
action: Action taken ("continue" or "complete").
|
|
330
|
+
plan_action: When set, show ``[keep]`` or ``[new]`` before the judgement text.
|
|
310
331
|
namespace: Event namespace.
|
|
311
332
|
verbosity_tier: Current verbosity tier.
|
|
312
333
|
|
|
@@ -315,8 +336,12 @@ def format_judgement(
|
|
|
315
336
|
"""
|
|
316
337
|
action_icon = "→" if action == "continue" else "✓"
|
|
317
338
|
|
|
339
|
+
badge = ""
|
|
340
|
+
if plan_action in ("keep", "new"):
|
|
341
|
+
badge = f"[{plan_action}] "
|
|
342
|
+
|
|
318
343
|
# Polish: Add "Reason:" prefix to make LLM reasoning prominent
|
|
319
|
-
content = f"🌀 {judgement}"
|
|
344
|
+
content = f"🌀 {badge}{judgement}"
|
|
320
345
|
|
|
321
346
|
return DisplayLine(
|
|
322
347
|
level=2, # Use level 2 for more prominence (like step headers)
|
|
@@ -329,38 +354,72 @@ def format_judgement(
|
|
|
329
354
|
|
|
330
355
|
|
|
331
356
|
def format_step_done(
|
|
332
|
-
description: str,
|
|
333
357
|
duration_s: float,
|
|
334
358
|
*,
|
|
335
359
|
tool_call_count: int = 0,
|
|
360
|
+
success: bool = True,
|
|
361
|
+
error_msg: str | None = None,
|
|
336
362
|
namespace: tuple[str, ...] = (),
|
|
337
363
|
verbosity_tier: VerbosityTier = VerbosityTier.NORMAL,
|
|
338
|
-
) -> DisplayLine:
|
|
339
|
-
"""Format
|
|
364
|
+
) -> list[DisplayLine]:
|
|
365
|
+
"""Format step completion as level-3 child node (IG-182).
|
|
366
|
+
|
|
367
|
+
IG-159/IG-182: Shows brief "Done"/"Failed" with tree connector as child of step header.
|
|
368
|
+
No description repeat - user already saw it in the step header above.
|
|
340
369
|
|
|
341
370
|
Args:
|
|
342
|
-
description: Step description (same as header).
|
|
343
371
|
duration_s: Duration in seconds.
|
|
344
372
|
tool_call_count: Number of tool calls made during step execution.
|
|
373
|
+
success: Whether step succeeded.
|
|
374
|
+
error_msg: Error message if failed.
|
|
345
375
|
namespace: Event namespace.
|
|
346
376
|
verbosity_tier: Current verbosity tier.
|
|
347
377
|
|
|
348
378
|
Returns:
|
|
349
|
-
DisplayLine for step
|
|
379
|
+
List of DisplayLine objects for step result tree (1-2 lines).
|
|
350
380
|
"""
|
|
351
381
|
duration_ms = int(duration_s * 1000)
|
|
352
|
-
# Abbreviate description for cleaner display
|
|
353
|
-
abbreviated = abbreviate_text(description, max_length=50)
|
|
354
382
|
tool_info = f" [{tool_call_count} tools]" if tool_call_count > 0 else ""
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
content=
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
383
|
+
|
|
384
|
+
# Success case: single line
|
|
385
|
+
if success:
|
|
386
|
+
content = f"Done{tool_info}"
|
|
387
|
+
return [
|
|
388
|
+
DisplayLine(
|
|
389
|
+
level=3, # Child node of step header (level 2)
|
|
390
|
+
content=content,
|
|
391
|
+
icon="|__", # Tree connector (IG-159)
|
|
392
|
+
indent=indent_for_level(3),
|
|
393
|
+
duration_ms=duration_ms,
|
|
394
|
+
source_prefix=_derive_source_prefix(namespace, verbosity_tier),
|
|
395
|
+
)
|
|
396
|
+
]
|
|
397
|
+
|
|
398
|
+
# Error case: result line + optional error detail
|
|
399
|
+
lines = [
|
|
400
|
+
DisplayLine(
|
|
401
|
+
level=3,
|
|
402
|
+
content=f"Failed{tool_info}",
|
|
403
|
+
icon="|__",
|
|
404
|
+
indent=indent_for_level(3),
|
|
405
|
+
duration_ms=duration_ms,
|
|
406
|
+
source_prefix=_derive_source_prefix(namespace, verbosity_tier),
|
|
407
|
+
)
|
|
408
|
+
]
|
|
409
|
+
|
|
410
|
+
# Show error message on level-4 line if present
|
|
411
|
+
if error_msg:
|
|
412
|
+
lines.append(
|
|
413
|
+
DisplayLine(
|
|
414
|
+
level=4, # Error detail as child of failed result
|
|
415
|
+
content=f"Error: {error_msg}",
|
|
416
|
+
icon="|__",
|
|
417
|
+
indent=indent_for_level(4),
|
|
418
|
+
source_prefix=_derive_source_prefix(namespace, verbosity_tier),
|
|
419
|
+
)
|
|
420
|
+
)
|
|
421
|
+
|
|
422
|
+
return lines
|
|
364
423
|
|
|
365
424
|
|
|
366
425
|
def format_goal_done(
|
|
@@ -402,6 +461,7 @@ __all__ = [
|
|
|
402
461
|
"format_goal_done",
|
|
403
462
|
"format_goal_header",
|
|
404
463
|
"format_judgement",
|
|
464
|
+
"format_plan_phase_reasoning",
|
|
405
465
|
"format_reasoning",
|
|
406
466
|
"format_step_done",
|
|
407
467
|
"format_step_header",
|