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.
Files changed (112) hide show
  1. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/.gitignore +1 -0
  2. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/PKG-INFO +3 -3
  3. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/pyproject.toml +4 -4
  4. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/cli/commands/autopilot_cmd.py +11 -12
  5. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/cli/commands/config_cmd.py +3 -3
  6. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/cli/commands/run_cmd.py +7 -11
  7. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/cli/commands/status_cmd.py +2 -2
  8. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/cli/commands/thread_cmd.py +7 -3
  9. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/cli/execution/daemon.py +2 -2
  10. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/cli/execution/launcher.py +0 -1
  11. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/cli/main.py +7 -12
  12. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/cli/renderer.py +6 -8
  13. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/cli/stream/display_line.py +8 -5
  14. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/cli/stream/formatter.py +78 -18
  15. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/cli/stream/pipeline.py +162 -41
  16. soothe_cli-0.3.3/src/soothe_cli/config/__init__.py +5 -0
  17. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/config/cli_config.py +29 -5
  18. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/shared/__init__.py +3 -1
  19. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/shared/command_router.py +9 -3
  20. soothe_cli-0.3.3/src/soothe_cli/shared/config_loader.py +64 -0
  21. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/shared/display_policy.py +22 -7
  22. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/shared/event_processor.py +35 -33
  23. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/shared/message_processing.py +90 -30
  24. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/shared/presentation_engine.py +1 -1
  25. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/shared/processor_state.py +1 -1
  26. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/shared/renderer_protocol.py +1 -1
  27. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/shared/rendering.py +1 -1
  28. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/shared/suppression_state.py +5 -4
  29. soothe_cli-0.3.3/src/soothe_cli/shared/tool_call_resolution.py +279 -0
  30. soothe_cli-0.3.3/src/soothe_cli/shared/tool_card_payload.py +121 -0
  31. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/shared/tool_formatters/fallback.py +2 -2
  32. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/shared/tool_formatters/structured.py +3 -3
  33. soothe_cli-0.3.3/src/soothe_cli/shared/tool_message_format.py +77 -0
  34. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/shared/tool_output_formatter.py +1 -1
  35. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/shared/tui_trace_log.py +1 -1
  36. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/app.py +66 -11
  37. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/command_registry.py +21 -0
  38. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/config.py +1 -1
  39. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/daemon_session.py +45 -1
  40. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/model_config.py +1 -1
  41. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/sessions.py +1 -1
  42. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/textual_adapter.py +601 -80
  43. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/theme.py +29 -2
  44. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/tool_display.py +111 -100
  45. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/update_check.py +1 -1
  46. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/widgets/autopilot_dashboard.py +4 -4
  47. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/widgets/autopilot_screen.py +1 -1
  48. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/widgets/chat_input.py +1 -1
  49. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/widgets/history.py +1 -1
  50. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/widgets/message_store.py +150 -0
  51. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/widgets/messages.py +674 -17
  52. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/widgets/thread_selector.py +3 -67
  53. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/widgets/welcome.py +5 -75
  54. soothe_cli-0.1.0/src/soothe_cli/config/__init__.py +0 -5
  55. soothe_cli-0.1.0/src/soothe_cli/shared/config_loader.py +0 -68
  56. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/README.md +0 -0
  57. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/__init__.py +0 -0
  58. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/cli/__init__.py +0 -0
  59. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/cli/commands/__init__.py +0 -0
  60. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/cli/commands/subagent_names.py +0 -0
  61. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/cli/execution/__init__.py +0 -0
  62. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/cli/execution/headless.py +0 -0
  63. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/cli/stream/__init__.py +0 -0
  64. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/cli/stream/context.py +0 -0
  65. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/cli/utils.py +0 -0
  66. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/plan/__init__.py +0 -0
  67. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/plan/rich_tree.py +0 -0
  68. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/shared/essential_events.py +0 -0
  69. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/shared/slash_commands.py +0 -0
  70. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/shared/subagent_routing.py +0 -0
  71. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/shared/tool_formatters/__init__.py +0 -0
  72. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/shared/tool_formatters/base.py +0 -0
  73. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/shared/tool_formatters/execution.py +0 -0
  74. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/shared/tool_formatters/file_ops.py +0 -0
  75. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/shared/tool_formatters/goal_formatter.py +0 -0
  76. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/shared/tool_formatters/media.py +0 -0
  77. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/shared/tool_formatters/web.py +0 -0
  78. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/__init__.py +0 -0
  79. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/_ask_user_types.py +0 -0
  80. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/_cli_context.py +0 -0
  81. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/_env_vars.py +0 -0
  82. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/_session_stats.py +0 -0
  83. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/_version.py +0 -0
  84. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/app.tcss +0 -0
  85. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/file_ops.py +0 -0
  86. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/formatting.py +0 -0
  87. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/hooks.py +0 -0
  88. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/input.py +0 -0
  89. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/media_utils.py +0 -0
  90. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/output.py +0 -0
  91. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/project_utils.py +0 -0
  92. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/skills/__init__.py +0 -0
  93. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/skills/invocation.py +0 -0
  94. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/skills/load.py +0 -0
  95. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/unicode_security.py +0 -0
  96. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/widgets/__init__.py +0 -0
  97. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/widgets/_links.py +0 -0
  98. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/widgets/approval.py +0 -0
  99. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/widgets/ask_user.py +0 -0
  100. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/widgets/autocomplete.py +0 -0
  101. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/widgets/clipboard.py +0 -0
  102. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/widgets/diff.py +0 -0
  103. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/widgets/editor.py +0 -0
  104. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/widgets/loading.py +0 -0
  105. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/widgets/mcp_viewer.py +0 -0
  106. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/widgets/model_selector.py +0 -0
  107. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/widgets/notification_settings.py +0 -0
  108. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/widgets/status.py +0 -0
  109. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/widgets/theme_selector.py +0 -0
  110. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/widgets/tool_renderers.py +0 -0
  111. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/widgets/tool_widgets.py +0 -0
  112. {soothe_cli-0.1.0 → soothe_cli-0.3.3}/src/soothe_cli/tui/widgets/tools.py +0 -0
@@ -217,3 +217,4 @@ plot_*
217
217
 
218
218
  checkpoint.json
219
219
  manifest.json
220
+ _bmad
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: soothe-cli
3
- Version: 0.1.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.2.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.1.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.2.0,<1.0.0", # WebSocket client, protocol, types
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, VERBOSITY_TO_LOG_LEVEL
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: Path to config file
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
- if verbosity is not None:
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, config_path=config, initial_prompt=prompt)
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 = VERBOSITY_TO_LOG_LEVEL.get(cfg.logging.verbosity, "INFO")
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, config_path=config)
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
@@ -11,7 +11,6 @@ def run_tui(
11
11
  cfg: CLIConfig,
12
12
  *,
13
13
  thread_id: str | None = None,
14
- config_path: str | None = None,
15
14
  initial_prompt: str | None = None,
16
15
  ) -> None:
17
16
  """Launch the Textual TUI (with daemon auto-start)."""
@@ -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, Literal # noqa: E402
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("--config", "-c", help="Path to configuration file."),
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 # Use custom CLI config
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
- # HARD SUPPRESS during multi-step execution (IG-143)
211
- if self._state.suppression.should_suppress_output():
212
- return
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
- # HARD SUPPRESS during multi-step execution (IG-143)
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(_level: int) -> str:
69
+ def indent_for_level(level: int) -> str:
70
70
  """Get indentation string for a display level.
71
71
 
72
- Headless CLI uses a flat information stream (no tree connectors).
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
- _level: Display level (1, 2, or 3); retained for API compatibility.
76
+ level: Display level (1=goal, 2=step/tool, 3=result child).
76
77
 
77
78
  Returns:
78
- Indentation string (always empty for stream layout).
79
+ Indentation string: "" for level 1-2, " " for level 3 (tree child).
79
80
  """
80
- return ""
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"💭 Reasoning: {reasoning}"
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 a step completion line with solid checkbox.
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 done with solid circle icon.
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
- content = f"✅ {abbreviated}{tool_info}"
356
- return DisplayLine(
357
- level=2,
358
- content=content,
359
- icon="●", # Solid circle for completed step
360
- indent=indent_for_level(2),
361
- duration_ms=duration_ms,
362
- source_prefix=_derive_source_prefix(namespace, verbosity_tier),
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",