soothe-cli 0.5.2__tar.gz → 0.5.4__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.5.2 → soothe_cli-0.5.4}/.gitignore +1 -5
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/PKG-INFO +1 -1
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/cli/commands/run_cmd.py +25 -7
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/cli/main.py +19 -4
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/config/cli_config.py +1 -1
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/_env_vars.py +4 -1
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/app/_history.py +4 -4
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/app/_messages_mixin.py +2 -2
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/app/_startup.py +19 -15
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/config.py +3 -3
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/textual_adapter/_turn.py +13 -4
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/update_check.py +46 -15
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/widgets/message_store.py +14 -14
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/widgets/messages.py +33 -17
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/widgets/welcome.py +23 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/README.md +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/pyproject.toml +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/__init__.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/cli/__init__.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/cli/commands/__init__.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/cli/commands/autopilot_cmd.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/cli/commands/loop_cmd.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/cli/execution/__init__.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/cli/execution/daemon.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/cli/execution/headless.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/cli/execution/headless_renderer.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/cli/execution/launcher.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/cli/stream/__init__.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/cli/stream/context.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/cli/stream/display_line.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/cli/stream/formatter.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/cli/stream/pipeline.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/cli/stream/task_scope.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/config/__init__.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/plan/__init__.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/plan/rich_tree.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/shared/__init__.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/shared/commands/__init__.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/shared/commands/command_router.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/shared/commands/slash_commands.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/shared/commands/subagent_routing.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/shared/config_loader.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/shared/core/__init__.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/shared/core/event_processor.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/shared/core/presentation_engine.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/shared/core/processor_state.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/shared/core/renderer_protocol.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/shared/duration_format.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/shared/events/__init__.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/shared/events/display_policy.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/shared/events/essential_events.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/shared/events/explore_task_display.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/shared/events/stream_accumulator.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/shared/events/tui_trace_log.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/shared/rendering/__init__.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/shared/rendering/async_renderer_protocol.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/shared/rendering/renderer_base.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/shared/tools/__init__.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/shared/tools/_utils.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/shared/tools/message_processing.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/shared/tools/rendering.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/shared/tools/tool_call_resolution.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/shared/tools/tool_card_payload.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/shared/tools/tool_card_visibility.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/shared/tools/tool_formatters/__init__.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/shared/tools/tool_formatters/base.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/shared/tools/tool_formatters/execution.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/shared/tools/tool_formatters/fallback.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/shared/tools/tool_formatters/file_ops.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/shared/tools/tool_formatters/goal_formatter.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/shared/tools/tool_formatters/media.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/shared/tools/tool_formatters/structured.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/shared/tools/tool_formatters/subagent.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/shared/tools/tool_formatters/web.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/shared/tools/tool_message_format.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/shared/tools/tool_output_formatter.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/__init__.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/_ask_user_types.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/_cli_context.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/_session_stats.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/_version.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/app/__init__.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/app/_app.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/app/_commands.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/app/_execution.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/app/_model.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/app/_module_init.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/app/_ui.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/app/app.tcss +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/command_registry.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/daemon_session.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/file_ops.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/formatting.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/hooks.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/input.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/media_utils.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/message_display_filter.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/model_config.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/output.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/preview_limits.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/project_utils.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/sessions.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/skills/__init__.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/skills/invocation.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/skills/load.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/textual_adapter/__init__.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/textual_adapter/_adapter.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/textual_adapter/_stream_formatting.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/textual_adapter/_stream_messages.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/textual_adapter/_turn_helpers.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/theme.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/tool_display.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/unicode_security.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/widgets/__init__.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/widgets/_links.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/widgets/approval.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/widgets/ask_user.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/widgets/autocomplete.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/widgets/autopilot_dashboard.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/widgets/autopilot_screen.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/widgets/chat_input.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/widgets/clipboard.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/widgets/diff.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/widgets/editor.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/widgets/history.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/widgets/loading.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/widgets/loop_selector.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/widgets/mcp_viewer.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/widgets/model_selector.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/widgets/notification_settings.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/widgets/status.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/widgets/theme_selector.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/widgets/tool_renderers.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/widgets/tool_widgets.py +0 -0
- {soothe_cli-0.5.2 → soothe_cli-0.5.4}/src/soothe_cli/tui/widgets/tools.py +0 -0
|
@@ -94,11 +94,7 @@ ipython_config.py
|
|
|
94
94
|
# install all needed dependencies.
|
|
95
95
|
#Pipfile.lock
|
|
96
96
|
|
|
97
|
-
# UV
|
|
98
|
-
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
99
|
-
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
100
|
-
# commonly ignored for libraries.
|
|
101
|
-
uv.lock
|
|
97
|
+
# UV — lockfile is committed for reproducible installs (Docker `uv sync --frozen`, CI).
|
|
102
98
|
.uv/
|
|
103
99
|
|
|
104
100
|
# poetry
|
|
@@ -24,25 +24,29 @@ def run_impl(
|
|
|
24
24
|
streaming_enabled: bool | None = None,
|
|
25
25
|
streaming_mode: str | None = None,
|
|
26
26
|
*,
|
|
27
|
+
tui_with_prompt: bool = False,
|
|
27
28
|
config_path: str | None = None,
|
|
28
29
|
) -> None:
|
|
29
30
|
"""Core implementation for running Soothe agent.
|
|
30
31
|
|
|
31
32
|
Args:
|
|
32
|
-
prompt: Optional prompt
|
|
33
|
+
prompt: Optional user message; non-empty prompt defaults to a headless
|
|
34
|
+
one-shot run unless ``tui_with_prompt`` is set or a loop is being
|
|
35
|
+
resumed (``resume_loop_id``).
|
|
33
36
|
resume_loop_id: Existing loop id to attach to (optional)
|
|
34
|
-
no_tui:
|
|
37
|
+
no_tui: Require headless mode (must include a non-empty prompt)
|
|
35
38
|
autonomous: Enable autonomous iteration mode
|
|
36
39
|
max_iterations: Max iterations for autonomous mode
|
|
37
40
|
streaming_enabled: Override daemon streaming enabled setting (RFC-614)
|
|
38
41
|
streaming_mode: Override daemon streaming mode ('streaming' or 'batch')
|
|
42
|
+
tui_with_prompt: When True with a prompt, open the TUI instead of headless.
|
|
39
43
|
"""
|
|
40
44
|
startup_start = time.perf_counter()
|
|
41
45
|
|
|
42
46
|
try:
|
|
43
47
|
cfg = load_config(config_path)
|
|
44
48
|
log_level = resolve_cli_log_level(logging_level=cfg.logging_level)
|
|
45
|
-
log_file = Path(SOOTHE_HOME) / "logs" / "
|
|
49
|
+
log_file = Path(SOOTHE_HOME) / "logs" / "cli.log"
|
|
46
50
|
setup_logging(log_level, log_file=log_file)
|
|
47
51
|
|
|
48
52
|
# PostgreSQL availability check (requires daemon-side config)
|
|
@@ -62,17 +66,31 @@ def run_impl(
|
|
|
62
66
|
|
|
63
67
|
run_start = time.perf_counter()
|
|
64
68
|
|
|
65
|
-
|
|
66
|
-
|
|
69
|
+
has_prompt = bool(prompt and str(prompt).strip())
|
|
70
|
+
attaching_loop = bool(resume_loop_id and str(resume_loop_id).strip())
|
|
71
|
+
|
|
72
|
+
if tui_with_prompt and has_prompt:
|
|
73
|
+
use_headless = False
|
|
74
|
+
elif no_tui and not has_prompt:
|
|
75
|
+
typer.echo(
|
|
76
|
+
"Error: --no-tui requires a non-empty --prompt (-p).",
|
|
77
|
+
err=True,
|
|
78
|
+
)
|
|
79
|
+
sys.exit(1)
|
|
80
|
+
elif no_tui:
|
|
81
|
+
use_headless = True
|
|
82
|
+
else:
|
|
83
|
+
use_headless = has_prompt and not attaching_loop
|
|
84
|
+
|
|
85
|
+
if use_headless:
|
|
67
86
|
run_headless(
|
|
68
87
|
cfg,
|
|
69
|
-
prompt
|
|
88
|
+
str(prompt).strip(),
|
|
70
89
|
resume_loop_id=resume_loop_id,
|
|
71
90
|
autonomous=autonomous,
|
|
72
91
|
max_iterations=max_iterations,
|
|
73
92
|
)
|
|
74
93
|
else:
|
|
75
|
-
# TUI mode (with optional initial prompt)
|
|
76
94
|
run_tui(cfg, resume_loop_id=resume_loop_id, initial_prompt=prompt)
|
|
77
95
|
|
|
78
96
|
run_elapsed_s = time.perf_counter() - run_start
|
|
@@ -55,12 +55,24 @@ def main(
|
|
|
55
55
|
prompt: Annotated[
|
|
56
56
|
str | None,
|
|
57
57
|
typer.Option(
|
|
58
|
-
"--prompt",
|
|
58
|
+
"--prompt",
|
|
59
|
+
"-p",
|
|
60
|
+
help="User message; runs a one-shot headless query by default (use --tui for TUI).",
|
|
59
61
|
),
|
|
60
62
|
] = None,
|
|
61
63
|
no_tui: Annotated[ # noqa: FBT002
|
|
62
64
|
bool,
|
|
63
|
-
typer.Option(
|
|
65
|
+
typer.Option(
|
|
66
|
+
"--no-tui",
|
|
67
|
+
help="Headless mode (requires --prompt). Same as default when -p is set.",
|
|
68
|
+
),
|
|
69
|
+
] = False,
|
|
70
|
+
tui_with_prompt: Annotated[ # noqa: FBT002
|
|
71
|
+
bool,
|
|
72
|
+
typer.Option(
|
|
73
|
+
"--tui",
|
|
74
|
+
help="With --prompt/-p, open the interactive TUI and auto-submit the prompt.",
|
|
75
|
+
),
|
|
64
76
|
] = False,
|
|
65
77
|
streaming: Annotated[
|
|
66
78
|
bool | None,
|
|
@@ -81,13 +93,15 @@ def main(
|
|
|
81
93
|
) -> None:
|
|
82
94
|
"""Soothe CLI - Intelligent AI assistant client.
|
|
83
95
|
|
|
84
|
-
Run without arguments for interactive TUI mode, or
|
|
96
|
+
Run without arguments for interactive TUI mode, or pass --prompt for a one-shot
|
|
97
|
+
headless query (stdout, then exit).
|
|
85
98
|
|
|
86
99
|
Note: This is the CLI client. Use 'soothed' command to manage the daemon server.
|
|
87
100
|
|
|
88
101
|
Examples:
|
|
89
102
|
soothe # Interactive TUI mode
|
|
90
|
-
soothe -p "Research AI advances" #
|
|
103
|
+
soothe -p "Research AI advances" # One-shot headless (non-TUI) query
|
|
104
|
+
soothe -p "Hello" --tui # TUI with an auto-submitted prompt
|
|
91
105
|
soothe loop list # List AgentLoop instances
|
|
92
106
|
"""
|
|
93
107
|
# Handle -h/--help flag
|
|
@@ -112,6 +126,7 @@ def main(
|
|
|
112
126
|
max_iterations=None,
|
|
113
127
|
streaming_enabled=streaming,
|
|
114
128
|
streaming_mode=streaming_mode,
|
|
129
|
+
tui_with_prompt=tui_with_prompt,
|
|
115
130
|
)
|
|
116
131
|
|
|
117
132
|
|
|
@@ -24,7 +24,7 @@ class CLIConfig:
|
|
|
24
24
|
daemon_host: str = "127.0.0.1"
|
|
25
25
|
daemon_port: int = 8765
|
|
26
26
|
|
|
27
|
-
# logging_level: DEBUG/INFO/… for ~/.soothe/logs/
|
|
27
|
+
# logging_level: DEBUG/INFO/… for ~/.soothe/logs/cli.log; None = default INFO.
|
|
28
28
|
logging_level: str | None = None
|
|
29
29
|
|
|
30
30
|
# Output streaming overrides (RFC-614)
|
|
@@ -29,7 +29,7 @@ from __future__ import annotations
|
|
|
29
29
|
# ---------------------------------------------------------------------------
|
|
30
30
|
|
|
31
31
|
AUTO_UPDATE = "SOOTHE_CLI_AUTO_UPDATE"
|
|
32
|
-
"""
|
|
32
|
+
"""Override automatic CLI updates: '1'/'true'/'yes' on, '0'/'false'/'no' off. On by default when unset."""
|
|
33
33
|
|
|
34
34
|
DEBUG = "SOOTHE_CLI_DEBUG"
|
|
35
35
|
"""Enable verbose debug logging to a file."""
|
|
@@ -43,6 +43,9 @@ EXTRA_SKILLS_DIRS = "SOOTHE_CLI_EXTRA_SKILLS_DIRS"
|
|
|
43
43
|
NO_UPDATE_CHECK = "SOOTHE_CLI_NO_UPDATE_CHECK"
|
|
44
44
|
"""Disable automatic update checking when set."""
|
|
45
45
|
|
|
46
|
+
UPDATE_CHECK = "SOOTHE_CLI_UPDATE_CHECK"
|
|
47
|
+
"""Force-enable startup PyPI update checks ('1', 'true', or 'yes'). On by default."""
|
|
48
|
+
|
|
46
49
|
SERVER_ENV_PREFIX = "SOOTHE_CLI_SERVER_"
|
|
47
50
|
"""Environment variable prefix used to pass CLI config to the server subprocess."""
|
|
48
51
|
|
|
@@ -438,10 +438,10 @@ class _HistoryMixin:
|
|
|
438
438
|
# (``phase=goal_completion``); avoid duplicate app-line noise.
|
|
439
439
|
return None
|
|
440
440
|
if event_type == "soothe.cognition.agent_loop.reasoned":
|
|
441
|
-
plan_action_raw = str(event_data.get("plan_action") or "
|
|
442
|
-
plan_action = plan_action_raw if plan_action_raw in {"keep", "new"} else "
|
|
441
|
+
plan_action_raw = str(event_data.get("plan_action") or "").strip()
|
|
442
|
+
plan_action = plan_action_raw if plan_action_raw in {"keep", "new"} else ""
|
|
443
443
|
return MessageData(
|
|
444
|
-
type=MessageType.
|
|
444
|
+
type=MessageType.COGNITION_REASON,
|
|
445
445
|
content="",
|
|
446
446
|
timestamp=event_timestamp,
|
|
447
447
|
cognition_plan_next_action=str(event_data.get("next_action") or ""),
|
|
@@ -541,7 +541,7 @@ class _HistoryMixin:
|
|
|
541
541
|
if msg_data is None:
|
|
542
542
|
continue
|
|
543
543
|
if msg_data.type not in (
|
|
544
|
-
MessageType.
|
|
544
|
+
MessageType.COGNITION_REASON,
|
|
545
545
|
MessageType.COGNITION_GOAL_TREE,
|
|
546
546
|
MessageType.STEP_PROGRESS,
|
|
547
547
|
):
|
|
@@ -28,7 +28,7 @@ from soothe_cli.tui.widgets.messages import (
|
|
|
28
28
|
AppMessage,
|
|
29
29
|
AssistantMessage,
|
|
30
30
|
CognitionGoalTreeMessage,
|
|
31
|
-
|
|
31
|
+
CognitionReasonMessage,
|
|
32
32
|
CognitionStepMessage,
|
|
33
33
|
ErrorMessage,
|
|
34
34
|
QueuedUserMessage,
|
|
@@ -153,7 +153,7 @@ class _MessagesMixin:
|
|
|
153
153
|
| ToolCallMessage
|
|
154
154
|
| SkillMessage
|
|
155
155
|
| CognitionStepMessage
|
|
156
|
-
|
|
|
156
|
+
| CognitionReasonMessage
|
|
157
157
|
| CognitionGoalTreeMessage,
|
|
158
158
|
) -> None:
|
|
159
159
|
"""Mount a message widget to the messages area.
|
|
@@ -153,8 +153,9 @@ class _StartupMixin:
|
|
|
153
153
|
group="daemon-connect",
|
|
154
154
|
)
|
|
155
155
|
|
|
156
|
-
# Background update check and what's-new banner
|
|
157
|
-
#
|
|
156
|
+
# Background update check and what's-new banner (on by default; opt-out via
|
|
157
|
+
# SOOTHE_CLI_NO_UPDATE_CHECK or [update].check: false; SOOTHE_CLI_UPDATE_CHECK
|
|
158
|
+
# forces on if config disables)
|
|
158
159
|
from soothe_cli.tui.update_check import is_update_check_enabled
|
|
159
160
|
|
|
160
161
|
if is_update_check_enabled():
|
|
@@ -603,14 +604,13 @@ class _StartupMixin:
|
|
|
603
604
|
return
|
|
604
605
|
|
|
605
606
|
self._update_available = (True, latest)
|
|
607
|
+
self.call_after_refresh(lambda v=latest: self._apply_welcome_update_notice(v))
|
|
606
608
|
except Exception:
|
|
607
609
|
logger.debug("Background update check failed", exc_info=True)
|
|
608
610
|
return
|
|
609
611
|
|
|
610
|
-
# Phase 2: auto-update
|
|
612
|
+
# Phase 2: optional auto-update (version notice lives on the welcome banner only)
|
|
611
613
|
try:
|
|
612
|
-
from soothe_cli.tui._version import __version__ as cli_version
|
|
613
|
-
|
|
614
614
|
if is_auto_update_enabled():
|
|
615
615
|
from soothe_cli.tui.update_check import perform_upgrade
|
|
616
616
|
|
|
@@ -626,6 +626,7 @@ class _StartupMixin:
|
|
|
626
626
|
severity="information",
|
|
627
627
|
timeout=10,
|
|
628
628
|
)
|
|
629
|
+
self.call_after_refresh(lambda: self._apply_welcome_update_notice(None))
|
|
629
630
|
else:
|
|
630
631
|
cmd = upgrade_command()
|
|
631
632
|
self.notify(
|
|
@@ -634,16 +635,6 @@ class _StartupMixin:
|
|
|
634
635
|
timeout=15,
|
|
635
636
|
markup=False,
|
|
636
637
|
)
|
|
637
|
-
else:
|
|
638
|
-
cmd = upgrade_command()
|
|
639
|
-
self.notify(
|
|
640
|
-
f"Update available: v{latest} (current: v{cli_version}). "
|
|
641
|
-
f"Run: {cmd}\n\n"
|
|
642
|
-
f"Enable auto-updates: /auto-update",
|
|
643
|
-
severity="information",
|
|
644
|
-
timeout=15,
|
|
645
|
-
markup=False,
|
|
646
|
-
)
|
|
647
638
|
except Exception:
|
|
648
639
|
logger.warning("Auto-update failed unexpectedly", exc_info=True)
|
|
649
640
|
self.notify(
|
|
@@ -652,6 +643,14 @@ class _StartupMixin:
|
|
|
652
643
|
timeout=10,
|
|
653
644
|
)
|
|
654
645
|
|
|
646
|
+
def _apply_welcome_update_notice(self, latest: str | None) -> None:
|
|
647
|
+
"""Show or hide the welcome-banner update line (must run on the UI thread)."""
|
|
648
|
+
try:
|
|
649
|
+
banner = self.query_one("#welcome-banner", WelcomeBanner)
|
|
650
|
+
banner.set_update_notice(latest)
|
|
651
|
+
except NoMatches:
|
|
652
|
+
logger.debug("Welcome banner not found while applying update notice")
|
|
653
|
+
|
|
655
654
|
async def _show_whats_new(self) -> None:
|
|
656
655
|
"""Show a 'what's new' banner on the first launch after an upgrade."""
|
|
657
656
|
try:
|
|
@@ -698,17 +697,22 @@ class _StartupMixin:
|
|
|
698
697
|
await self._mount_message(AppMessage("Checking for updates..."))
|
|
699
698
|
available, latest = await asyncio.to_thread(is_update_available, bypass_cache=True)
|
|
700
699
|
if not available:
|
|
700
|
+
self._update_available = (False, None)
|
|
701
|
+
self._apply_welcome_update_notice(None)
|
|
701
702
|
await self._mount_message(AppMessage("Already on the latest version."))
|
|
702
703
|
return
|
|
703
704
|
|
|
704
705
|
from soothe_cli.tui._version import __version__ as cli_version
|
|
705
706
|
|
|
707
|
+
self._update_available = (True, latest)
|
|
708
|
+
self._apply_welcome_update_notice(latest)
|
|
706
709
|
await self._mount_message(
|
|
707
710
|
AppMessage(f"Update available: v{latest} (current: v{cli_version}). Upgrading...")
|
|
708
711
|
)
|
|
709
712
|
success, output = await perform_upgrade()
|
|
710
713
|
if success:
|
|
711
714
|
self._update_available = (False, None)
|
|
715
|
+
self._apply_welcome_update_notice(None)
|
|
712
716
|
await self._mount_message(
|
|
713
717
|
AppMessage(f"Updated to v{latest}. Restart to use the new version.")
|
|
714
718
|
)
|
|
@@ -252,7 +252,7 @@ class Glyphs:
|
|
|
252
252
|
|
|
253
253
|
# Expand/collapse icons
|
|
254
254
|
expand: str # ▶ vs [+] - shown when collapsed (click to expand)
|
|
255
|
-
collapse: str #
|
|
255
|
+
collapse: str # ▼ vs [v] - shown when expanded (click to collapse)
|
|
256
256
|
|
|
257
257
|
# Box-drawing characters
|
|
258
258
|
box_vertical: str # │ vs |
|
|
@@ -287,7 +287,7 @@ UNICODE_GLYPHS = Glyphs(
|
|
|
287
287
|
assistant="🤖", # AI/assistant icon
|
|
288
288
|
# Expand/collapse icons
|
|
289
289
|
expand="▶",
|
|
290
|
-
collapse="
|
|
290
|
+
collapse="▼",
|
|
291
291
|
# Box-drawing characters
|
|
292
292
|
box_vertical="│",
|
|
293
293
|
box_horizontal="─",
|
|
@@ -318,7 +318,7 @@ ASCII_GLYPHS = Glyphs(
|
|
|
318
318
|
assistant="[A]", # AI/assistant icon (ASCII)
|
|
319
319
|
# Expand/collapse icons
|
|
320
320
|
expand="[+]",
|
|
321
|
-
collapse="[
|
|
321
|
+
collapse="[v]",
|
|
322
322
|
# Box-drawing characters
|
|
323
323
|
box_vertical="|",
|
|
324
324
|
box_horizontal="-",
|
|
@@ -91,7 +91,7 @@ from soothe_cli.tui.textual_adapter._turn_helpers import (
|
|
|
91
91
|
from soothe_cli.tui.widgets.messages import (
|
|
92
92
|
AppMessage,
|
|
93
93
|
AssistantMessage,
|
|
94
|
-
|
|
94
|
+
CognitionReasonMessage,
|
|
95
95
|
CognitionStepMessage,
|
|
96
96
|
DiffMessage,
|
|
97
97
|
SummarizationMessage,
|
|
@@ -1249,6 +1249,8 @@ async def execute_task_textual(
|
|
|
1249
1249
|
error_text = str(
|
|
1250
1250
|
data.get("error") or data.get("message") or "Agent error"
|
|
1251
1251
|
)
|
|
1252
|
+
adapter.finalize_pending_tools_with_error(error_text)
|
|
1253
|
+
adapter.finalize_pending_steps_with_error(error_text)
|
|
1252
1254
|
await adapter._mount_message(AppMessage(error_text))
|
|
1253
1255
|
if adapter._set_spinner:
|
|
1254
1256
|
await adapter._set_spinner(None)
|
|
@@ -1395,9 +1397,9 @@ async def execute_task_textual(
|
|
|
1395
1397
|
)
|
|
1396
1398
|
pending_text_by_namespace[ns_key] = ""
|
|
1397
1399
|
assistant_message_by_namespace.pop(ns_key, None)
|
|
1398
|
-
pa_raw = data.get("plan_action", "
|
|
1399
|
-
plan_action = pa_raw if pa_raw in ("keep", "new") else "
|
|
1400
|
-
plan_widget =
|
|
1400
|
+
pa_raw = data.get("plan_action", "")
|
|
1401
|
+
plan_action = pa_raw if pa_raw in ("keep", "new") else ""
|
|
1402
|
+
plan_widget = CognitionReasonMessage(
|
|
1401
1403
|
next_action=str(data.get("next_action", "")),
|
|
1402
1404
|
status=str(data.get("status", "")),
|
|
1403
1405
|
iteration=int(data.get("iteration", 0)),
|
|
@@ -1509,6 +1511,13 @@ async def execute_task_textual(
|
|
|
1509
1511
|
)
|
|
1510
1512
|
adapter._pending_main_tools.clear()
|
|
1511
1513
|
|
|
1514
|
+
# Safety net: finalize any steps/tools still in-flight (e.g. worker
|
|
1515
|
+
# crash sent a soothe.error.* event but step_completed was never
|
|
1516
|
+
# emitted, or stream ended before matching results arrived).
|
|
1517
|
+
if adapter._current_step_messages or adapter._current_tool_messages:
|
|
1518
|
+
adapter.finalize_pending_tools_with_error("Stream ended unexpectedly")
|
|
1519
|
+
adapter.finalize_pending_steps_with_error("Stream ended unexpectedly")
|
|
1520
|
+
|
|
1512
1521
|
# Handle HITL after stream completes
|
|
1513
1522
|
if interrupt_occurred:
|
|
1514
1523
|
any_rejected = False
|
|
@@ -316,35 +316,66 @@ async def perform_upgrade() -> tuple[bool, str]:
|
|
|
316
316
|
|
|
317
317
|
|
|
318
318
|
def is_update_check_enabled() -> bool:
|
|
319
|
-
"""Return whether update checks are enabled.
|
|
319
|
+
"""Return whether startup update checks are enabled.
|
|
320
320
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
321
|
+
Disabled when `SOOTHE_CLI_NO_UPDATE_CHECK` or legacy `SOOTHE_NO_UPDATE_CHECK`
|
|
322
|
+
is set. When `SOOTHE_CLI_UPDATE_CHECK` is ``1``/``true``/``yes``, checks are
|
|
323
|
+
enabled (including when ``[update].check: false`` would otherwise turn them
|
|
324
|
+
off). Otherwise, respects ``[update].check`` in ``config.yml`` when
|
|
325
|
+
present; defaults to on. Use ``/update`` to check manually any time.
|
|
325
326
|
"""
|
|
326
|
-
|
|
327
|
+
from soothe_cli.tui._env_vars import NO_UPDATE_CHECK, UPDATE_CHECK
|
|
328
|
+
|
|
329
|
+
if os.environ.get("SOOTHE_NO_UPDATE_CHECK") or os.environ.get(NO_UPDATE_CHECK):
|
|
327
330
|
return False
|
|
328
|
-
|
|
331
|
+
if os.environ.get(UPDATE_CHECK, "").lower() in {"1", "true", "yes"}:
|
|
332
|
+
return True
|
|
333
|
+
cfg = _read_update_config()
|
|
334
|
+
if "check" in cfg:
|
|
335
|
+
return bool(cfg["check"])
|
|
336
|
+
return True
|
|
329
337
|
|
|
330
338
|
|
|
331
|
-
def
|
|
332
|
-
"""Return
|
|
339
|
+
def _auto_update_env_override() -> bool | None:
|
|
340
|
+
"""Return env-forced auto-update flag, or ``None`` if unset.
|
|
341
|
+
|
|
342
|
+
``SOOTHE_CLI_AUTO_UPDATE`` (and legacy ``SOOTHE_AUTO_UPDATE``) may be
|
|
343
|
+
``1``/``true``/``yes`` to force on or ``0``/``false``/``no`` to force off.
|
|
344
|
+
"""
|
|
345
|
+
from soothe_cli.tui._env_vars import AUTO_UPDATE
|
|
333
346
|
|
|
334
|
-
|
|
335
|
-
|
|
347
|
+
for key in (AUTO_UPDATE, "SOOTHE_AUTO_UPDATE"):
|
|
348
|
+
raw = os.environ.get(key)
|
|
349
|
+
if raw is None or not str(raw).strip():
|
|
350
|
+
continue
|
|
351
|
+
lv = str(raw).strip().lower()
|
|
352
|
+
if lv in {"1", "true", "yes"}:
|
|
353
|
+
return True
|
|
354
|
+
if lv in {"0", "false", "no"}:
|
|
355
|
+
return False
|
|
356
|
+
return None
|
|
336
357
|
|
|
337
|
-
|
|
358
|
+
|
|
359
|
+
def is_auto_update_enabled() -> bool:
|
|
360
|
+
"""Return whether auto-update is enabled.
|
|
338
361
|
|
|
339
362
|
Always disabled for editable installs.
|
|
363
|
+
|
|
364
|
+
Otherwise, ``SOOTHE_CLI_AUTO_UPDATE`` (or legacy ``SOOTHE_AUTO_UPDATE``)
|
|
365
|
+
forces on or off when set. When unset, ``[update].auto_update`` in
|
|
366
|
+
``config.yml`` is used if present; defaults to on.
|
|
340
367
|
"""
|
|
341
368
|
from soothe_cli.tui.config import _is_editable_install
|
|
342
369
|
|
|
343
370
|
if _is_editable_install():
|
|
344
371
|
return False
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
372
|
+
env_val = _auto_update_env_override()
|
|
373
|
+
if env_val is not None:
|
|
374
|
+
return env_val
|
|
375
|
+
cfg = _read_update_config()
|
|
376
|
+
if "auto_update" in cfg:
|
|
377
|
+
return bool(cfg["auto_update"])
|
|
378
|
+
return True
|
|
348
379
|
|
|
349
380
|
|
|
350
381
|
def set_auto_update(enabled: bool) -> None:
|
|
@@ -53,7 +53,7 @@ class MessageType(StrEnum):
|
|
|
53
53
|
APP = "app"
|
|
54
54
|
SUMMARIZATION = "summarization"
|
|
55
55
|
STEP_PROGRESS = "step_progress"
|
|
56
|
-
|
|
56
|
+
COGNITION_REASON = "cognition_reason"
|
|
57
57
|
COGNITION_GOAL_TREE = "cognition_goal_tree"
|
|
58
58
|
DIFF = "diff"
|
|
59
59
|
|
|
@@ -163,22 +163,22 @@ class MessageData:
|
|
|
163
163
|
"""JSON list of tool rows from ``CognitionStepMessage.snapshot_tool_rows()`` (IG-402)."""
|
|
164
164
|
|
|
165
165
|
cognition_plan_next_action: str | None = None
|
|
166
|
-
"""User-facing next step (
|
|
166
|
+
"""User-facing next step (COGNITION_REASON only)."""
|
|
167
167
|
|
|
168
168
|
cognition_plan_status: str | None = None
|
|
169
|
-
"""Plan status: continue, replan, done (
|
|
169
|
+
"""Plan status: continue, replan, done (COGNITION_REASON only)."""
|
|
170
170
|
|
|
171
171
|
cognition_plan_iteration: int | None = None
|
|
172
|
-
"""Agent-loop iteration (
|
|
172
|
+
"""Agent-loop iteration (COGNITION_REASON only)."""
|
|
173
173
|
|
|
174
174
|
cognition_plan_action: str | None = None
|
|
175
|
-
"""``keep`` or ``new`` (
|
|
175
|
+
"""``keep`` or ``new`` (COGNITION_REASON only)."""
|
|
176
176
|
|
|
177
177
|
cognition_plan_assessment: str | None = None
|
|
178
|
-
"""Phase-1 assessment text (
|
|
178
|
+
"""Phase-1 assessment text (COGNITION_REASON only)."""
|
|
179
179
|
|
|
180
180
|
cognition_plan_strategy: str | None = None
|
|
181
|
-
"""Phase-2 plan reasoning (
|
|
181
|
+
"""Phase-2 plan reasoning (COGNITION_REASON only)."""
|
|
182
182
|
|
|
183
183
|
cognition_goal_snapshot_json: str | None = None
|
|
184
184
|
"""JSON blob from ``CognitionGoalTreeMessage.snapshot_dict()`` (COGNITION_GOAL_TREE only)."""
|
|
@@ -227,7 +227,7 @@ class MessageData:
|
|
|
227
227
|
AppMessage,
|
|
228
228
|
AssistantMessage,
|
|
229
229
|
CognitionGoalTreeMessage,
|
|
230
|
-
|
|
230
|
+
CognitionReasonMessage,
|
|
231
231
|
CognitionStepMessage,
|
|
232
232
|
DiffMessage,
|
|
233
233
|
ErrorMessage,
|
|
@@ -329,12 +329,12 @@ class MessageData:
|
|
|
329
329
|
)
|
|
330
330
|
return w
|
|
331
331
|
|
|
332
|
-
case MessageType.
|
|
333
|
-
return
|
|
332
|
+
case MessageType.COGNITION_REASON:
|
|
333
|
+
return CognitionReasonMessage(
|
|
334
334
|
next_action=self.cognition_plan_next_action or "",
|
|
335
335
|
status=self.cognition_plan_status or "",
|
|
336
336
|
iteration=int(self.cognition_plan_iteration or 0),
|
|
337
|
-
plan_action=self.cognition_plan_action or "
|
|
337
|
+
plan_action=self.cognition_plan_action or "",
|
|
338
338
|
assessment_reasoning=self.cognition_plan_assessment or "",
|
|
339
339
|
plan_reasoning=self.cognition_plan_strategy or "",
|
|
340
340
|
id=self.id,
|
|
@@ -386,7 +386,7 @@ class MessageData:
|
|
|
386
386
|
AppMessage,
|
|
387
387
|
AssistantMessage,
|
|
388
388
|
CognitionGoalTreeMessage,
|
|
389
|
-
|
|
389
|
+
CognitionReasonMessage,
|
|
390
390
|
CognitionStepMessage,
|
|
391
391
|
DiffMessage,
|
|
392
392
|
ErrorMessage,
|
|
@@ -414,9 +414,9 @@ class MessageData:
|
|
|
414
414
|
cognition_goal_snapshot_json=json.dumps(widget.snapshot_dict()),
|
|
415
415
|
)
|
|
416
416
|
|
|
417
|
-
if isinstance(widget,
|
|
417
|
+
if isinstance(widget, CognitionReasonMessage):
|
|
418
418
|
return cls(
|
|
419
|
-
type=MessageType.
|
|
419
|
+
type=MessageType.COGNITION_REASON,
|
|
420
420
|
content="",
|
|
421
421
|
id=widget_id,
|
|
422
422
|
cognition_plan_next_action=widget._next_action,
|