soothe-cli 0.7.11__tar.gz → 0.7.12__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.7.11 → soothe_cli-0.7.12}/PKG-INFO +1 -1
- soothe_cli-0.7.12/src/soothe_cli/__init__.py +5 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/cli/main.py +2 -2
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/runtime/presentation/duration_format.py +9 -2
- soothe_cli-0.7.12/src/soothe_cli/tui/_version.py +128 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/app/_execution.py +2 -29
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/app/_startup.py +3 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/config.py +13 -86
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/widgets/loading.py +3 -3
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/widgets/messages/cognition_goal_tree.py +2 -3
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/widgets/messages/cognition_step_activity.py +2 -2
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/widgets/plan_quick_view_overlay.py +1 -0
- soothe_cli-0.7.11/src/soothe_cli/__init__.py +0 -10
- soothe_cli-0.7.11/src/soothe_cli/tui/_version.py +0 -21
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/.gitignore +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/README.md +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/pyproject.toml +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/cli/__init__.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/cli/commands/__init__.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/cli/commands/autopilot_cmd.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/cli/commands/config_cmd.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/cli/commands/cron_cmd.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/cli/commands/loop_cmd.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/cli/commands/run_cmd.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/cli/commands/status_cmd.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/cli/execution/__init__.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/cli/execution/daemon.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/cli/execution/daemon_errors.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/cli/execution/headless.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/cli/execution/headless_renderer.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/cli/execution/launcher.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/config/__init__.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/config/cli_config.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/config/loader.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/config/logging_setup.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/runtime/__init__.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/runtime/headless/processor.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/runtime/headless/processor_state.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/runtime/parse/message_processing.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/runtime/parse/tool_call_resolution.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/runtime/parse/tool_result.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/runtime/policy/display_policy.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/runtime/policy/essential_events.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/runtime/policy/tui_trace_log.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/runtime/presentation/engine.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/runtime/presentation/renderer_base.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/runtime/presentation/renderer_protocol.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/runtime/presentation/subagent_task_display.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/runtime/state/file_tracker.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/runtime/state/session_stats.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/runtime/state/step_router.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/runtime/state/stream_accumulator.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/runtime/state/transcript.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/runtime/transport/session.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/runtime/turn/pipeline.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/runtime/turn/prepare.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/runtime/wire/chunk_filter.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/runtime/wire/message_text.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/runtime/wire/messages.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/__init__.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/_cli_context.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/_env_vars.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/app/__init__.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/app/_app.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/app/_history.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/app/_messages_mixin.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/app/_model.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/app/_module_init.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/app/_ui.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/app/app.tcss +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/binding.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/command_registry.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/commands/__init__.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/commands/command_router.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/commands/slash_commands.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/commands/subagent_routing.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/file_change_notify.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/file_change_renderers.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/hooks.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/input.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/markdown_theme.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/media_utils.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/model_config.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/path_utils.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/preview_limits.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/project_utils.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/sessions.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/shell_color.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/skills/__init__.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/skills/invocation.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/skills/load.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/spinner_labels.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/textual_adapter.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/theme.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/tips.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/tool_display.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/unicode_security.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/update_check.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/widgets/__init__.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/widgets/_links.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/widgets/autocomplete.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/widgets/chat_input.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/widgets/clipboard.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/widgets/context_viewer.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/widgets/diff.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/widgets/editor.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/widgets/file_change_preview.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/widgets/help_screen.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/widgets/history.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/widgets/loop_selector.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/widgets/mcp_viewer.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/widgets/message_store.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/widgets/messages/__init__.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/widgets/messages/_helpers.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/widgets/messages/app.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/widgets/messages/assistant.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/widgets/messages/clarification.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/widgets/messages/cognition_reason.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/widgets/messages/cognition_step.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/widgets/messages/cognition_subagent.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/widgets/messages/diff_message.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/widgets/messages/error.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/widgets/messages/skill.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/widgets/messages/user.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/widgets/model_selector.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/widgets/notification_settings.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/widgets/status.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/widgets/theme_selector.py +0 -0
- {soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/widgets/welcome.py +0 -0
|
@@ -6,7 +6,6 @@ from dotenv import load_dotenv
|
|
|
6
6
|
|
|
7
7
|
load_dotenv()
|
|
8
8
|
|
|
9
|
-
from importlib.metadata import version # noqa: E402
|
|
10
9
|
from pathlib import Path # noqa: E402
|
|
11
10
|
from typing import Annotated # noqa: E402
|
|
12
11
|
|
|
@@ -15,6 +14,7 @@ from soothe_sdk.client.config import SOOTHE_HOME # noqa: E402
|
|
|
15
14
|
|
|
16
15
|
from soothe_cli.config.cli_config import CLIConfig # noqa: E402
|
|
17
16
|
from soothe_cli.config.loader import set_runtime_config # noqa: E402
|
|
17
|
+
from soothe_cli.tui._version import __version__ # noqa: E402
|
|
18
18
|
from soothe_cli.tui.markdown_theme import ( # noqa: E402
|
|
19
19
|
DEFAULT_MARKDOWN_THEME,
|
|
20
20
|
REGISTRY,
|
|
@@ -177,7 +177,7 @@ def main(
|
|
|
177
177
|
|
|
178
178
|
# Handle --version flag
|
|
179
179
|
if show_version:
|
|
180
|
-
typer.echo(f"soothe {
|
|
180
|
+
typer.echo(f"soothe {__version__}")
|
|
181
181
|
raise typer.Exit
|
|
182
182
|
|
|
183
183
|
home_path = Path(soothe_home).expanduser() if soothe_home else Path(SOOTHE_HOME)
|
{soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/runtime/presentation/duration_format.py
RENAMED
|
@@ -4,13 +4,15 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
def format_duration(seconds: float) -> str:
|
|
7
|
-
"""Format a duration in seconds into a human-readable string.
|
|
7
|
+
"""Format a completed duration in seconds into a human-readable string.
|
|
8
8
|
|
|
9
9
|
Args:
|
|
10
10
|
seconds: Duration in seconds.
|
|
11
11
|
|
|
12
12
|
Returns:
|
|
13
|
-
Formatted string like
|
|
13
|
+
Formatted string like ``5s``, ``2.3s``, ``5m 12s``, or ``1h 23m 4s``.
|
|
14
|
+
For live running timers that tick in whole seconds, use
|
|
15
|
+
:func:`format_running_elapsed` instead.
|
|
14
16
|
"""
|
|
15
17
|
rounded = round(seconds, 1)
|
|
16
18
|
if rounded < 60: # noqa: PLR2004
|
|
@@ -24,6 +26,11 @@ def format_duration(seconds: float) -> str:
|
|
|
24
26
|
return f"{hours}h {minutes}m {secs}s"
|
|
25
27
|
|
|
26
28
|
|
|
29
|
+
def format_running_elapsed(seconds: float) -> str:
|
|
30
|
+
"""Format a live running timer using whole-second ticks (e.g. ``20s``, ``1m 5s``)."""
|
|
31
|
+
return format_duration(float(max(0, int(seconds))))
|
|
32
|
+
|
|
33
|
+
|
|
27
34
|
def format_duration_ms(milliseconds: int) -> str:
|
|
28
35
|
"""Format a wall-clock duration in milliseconds for status lines and cards.
|
|
29
36
|
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"""Version information and lightweight constants for `soothe`."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import logging
|
|
7
|
+
import re
|
|
8
|
+
from importlib.metadata import PackageNotFoundError, distribution, version
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from urllib.parse import unquote, urlparse
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
_VERSION_PATTERN = re.compile(r"^(\d+\.\d+\.\d+)")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _parse_version_file_text(text: str) -> str | None:
|
|
18
|
+
"""Extract the semver from a repo ``VERSION`` file."""
|
|
19
|
+
for line in text.splitlines():
|
|
20
|
+
stripped = line.strip()
|
|
21
|
+
if not stripped or stripped.startswith("#"):
|
|
22
|
+
continue
|
|
23
|
+
match = _VERSION_PATTERN.match(stripped)
|
|
24
|
+
if match:
|
|
25
|
+
return match.group(1)
|
|
26
|
+
return None
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _editable_source_roots() -> list[Path]:
|
|
30
|
+
"""Return resolved source roots for editable Soothe packages."""
|
|
31
|
+
roots: list[Path] = []
|
|
32
|
+
seen: set[Path] = set()
|
|
33
|
+
for dist_name in ("soothe-cli", "soothe", "Soothe"):
|
|
34
|
+
try:
|
|
35
|
+
dist = distribution(dist_name)
|
|
36
|
+
except PackageNotFoundError:
|
|
37
|
+
continue
|
|
38
|
+
try:
|
|
39
|
+
raw = dist.read_text("direct_url.json")
|
|
40
|
+
except (FileNotFoundError, OSError):
|
|
41
|
+
continue
|
|
42
|
+
if not raw:
|
|
43
|
+
continue
|
|
44
|
+
try:
|
|
45
|
+
data = json.loads(raw)
|
|
46
|
+
except json.JSONDecodeError:
|
|
47
|
+
logger.debug("Invalid direct_url.json for %s", dist_name, exc_info=True)
|
|
48
|
+
continue
|
|
49
|
+
if not data.get("dir_info", {}).get("editable"):
|
|
50
|
+
continue
|
|
51
|
+
url = data.get("url", "")
|
|
52
|
+
if not url.startswith("file://"):
|
|
53
|
+
continue
|
|
54
|
+
root = Path(unquote(urlparse(url).path)).resolve()
|
|
55
|
+
if root not in seen:
|
|
56
|
+
seen.add(root)
|
|
57
|
+
roots.append(root)
|
|
58
|
+
return roots
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _read_repo_version_from_source(root: Path) -> str | None:
|
|
62
|
+
"""Read the monorepo ``VERSION`` file for an editable install root."""
|
|
63
|
+
candidates = [root.parent.parent / "VERSION", *(parent / "VERSION" for parent in root.parents)]
|
|
64
|
+
for path in candidates:
|
|
65
|
+
if not path.is_file():
|
|
66
|
+
continue
|
|
67
|
+
try:
|
|
68
|
+
parsed = _parse_version_file_text(path.read_text(encoding="utf-8"))
|
|
69
|
+
except OSError:
|
|
70
|
+
logger.debug("Failed to read VERSION file at %s", path, exc_info=True)
|
|
71
|
+
continue
|
|
72
|
+
if parsed:
|
|
73
|
+
return parsed
|
|
74
|
+
return None
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _resolve_version() -> str:
|
|
78
|
+
"""Resolve the CLI version, preferring repo ``VERSION`` for editable installs."""
|
|
79
|
+
for root in _editable_source_roots():
|
|
80
|
+
repo_version = _read_repo_version_from_source(root)
|
|
81
|
+
if repo_version:
|
|
82
|
+
return repo_version
|
|
83
|
+
|
|
84
|
+
for pkg in ("soothe-cli", "soothe"):
|
|
85
|
+
try:
|
|
86
|
+
return version(pkg)
|
|
87
|
+
except PackageNotFoundError:
|
|
88
|
+
continue
|
|
89
|
+
return "0.0.0"
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def is_editable_install() -> bool:
|
|
93
|
+
"""Return whether Soothe is installed in editable mode."""
|
|
94
|
+
return bool(_editable_source_roots())
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def editable_install_display_path() -> str | None:
|
|
98
|
+
"""Return a ``~``-contracted source path for editable installs."""
|
|
99
|
+
roots = _editable_source_roots()
|
|
100
|
+
if not roots:
|
|
101
|
+
return None
|
|
102
|
+
|
|
103
|
+
install_root = roots[0]
|
|
104
|
+
for root in roots:
|
|
105
|
+
if root.name == "soothe-cli":
|
|
106
|
+
install_root = root
|
|
107
|
+
break
|
|
108
|
+
|
|
109
|
+
path = str(install_root)
|
|
110
|
+
home = str(Path.home())
|
|
111
|
+
if path.startswith(home):
|
|
112
|
+
return "~" + path[len(home) :]
|
|
113
|
+
return path
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
__version__ = _resolve_version()
|
|
117
|
+
|
|
118
|
+
DOCS_URL = "https://github.com/mirasoth/soothe/docs"
|
|
119
|
+
"""URL for Soothe documentation."""
|
|
120
|
+
|
|
121
|
+
PYPI_URL = "https://pypi.org/pypi/soothe/json"
|
|
122
|
+
"""PyPI JSON API endpoint for version checks."""
|
|
123
|
+
|
|
124
|
+
CHANGELOG_URL = "https://github.com/mirasoth/soothe/blob/main/CHANGELOG.md"
|
|
125
|
+
"""URL for the full changelog."""
|
|
126
|
+
|
|
127
|
+
USER_AGENT = f"soothe/{__version__} update-check"
|
|
128
|
+
"""User-Agent header sent with PyPI requests."""
|
|
@@ -533,36 +533,9 @@ class _ExecutionMixin:
|
|
|
533
533
|
await self._open_url_command(command, cmd)
|
|
534
534
|
elif cmd == "/version":
|
|
535
535
|
await self._mount_message(UserMessage(command))
|
|
536
|
-
|
|
537
|
-
try:
|
|
538
|
-
from soothe_cli.tui._version import (
|
|
539
|
-
__version__ as cli_version,
|
|
540
|
-
)
|
|
541
|
-
|
|
542
|
-
cli_line = f"Soothe version: {cli_version}"
|
|
543
|
-
except ImportError:
|
|
544
|
-
logger.debug("soothe._version module not found")
|
|
545
|
-
cli_line = "Soothe version: unknown"
|
|
546
|
-
except Exception:
|
|
547
|
-
logger.warning("Unexpected error looking up CLI version", exc_info=True)
|
|
548
|
-
cli_line = "Soothe version: unknown"
|
|
549
|
-
try:
|
|
550
|
-
from importlib.metadata import (
|
|
551
|
-
PackageNotFoundError,
|
|
552
|
-
)
|
|
553
|
-
from importlib.metadata import (
|
|
554
|
-
version as _pkg_version,
|
|
555
|
-
)
|
|
536
|
+
from soothe_cli.tui._version import __version__ as cli_version
|
|
556
537
|
|
|
557
|
-
|
|
558
|
-
sdk_line = f"Soothe (SDK) version: {sdk_version}"
|
|
559
|
-
except PackageNotFoundError:
|
|
560
|
-
logger.debug("Soothe SDK package not found in environment")
|
|
561
|
-
sdk_line = "Soothe (SDK) version: unknown"
|
|
562
|
-
except Exception:
|
|
563
|
-
logger.warning("Unexpected error looking up SDK version", exc_info=True)
|
|
564
|
-
sdk_line = "Soothe (SDK) version: unknown"
|
|
565
|
-
await self._mount_message(AppMessage(f"{cli_line}\n{sdk_line}"))
|
|
538
|
+
await self._mount_message(AppMessage(f"Soothe version: {cli_version}"))
|
|
566
539
|
elif cmd == "/clear":
|
|
567
540
|
self._pending_messages.clear()
|
|
568
541
|
self._queued_widgets.clear()
|
|
@@ -119,6 +119,9 @@ class _StartupMixin:
|
|
|
119
119
|
gc.freeze()
|
|
120
120
|
|
|
121
121
|
chat = self.query_one("#chat", VerticalScroll)
|
|
122
|
+
# Transcript scrolling is mouse-driven; keeping the region unfocusable
|
|
123
|
+
# prevents clicks in the message area from stealing the chat prompt caret.
|
|
124
|
+
chat.can_focus = False
|
|
122
125
|
chat.anchor()
|
|
123
126
|
|
|
124
127
|
self._status_bar = self.query_one("#status-bar", StatusBar)
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import importlib
|
|
6
|
-
import json
|
|
7
6
|
import logging
|
|
8
7
|
import os
|
|
9
8
|
import re
|
|
@@ -11,14 +10,19 @@ import sys
|
|
|
11
10
|
import threading
|
|
12
11
|
from dataclasses import dataclass
|
|
13
12
|
from enum import StrEnum
|
|
14
|
-
from importlib.metadata import PackageNotFoundError, distribution
|
|
15
13
|
from pathlib import Path
|
|
16
14
|
from typing import TYPE_CHECKING, Any
|
|
17
|
-
from urllib.parse import unquote, urlparse
|
|
18
15
|
|
|
19
16
|
from soothe_sdk.client.config import SOOTHE_HOME
|
|
20
17
|
|
|
21
|
-
from soothe_cli.tui._version import
|
|
18
|
+
from soothe_cli.tui._version import (
|
|
19
|
+
__version__,
|
|
20
|
+
)
|
|
21
|
+
from soothe_cli.tui._version import (
|
|
22
|
+
is_editable_install as _is_editable_install,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
__all__ = ["_is_editable_install"]
|
|
22
26
|
|
|
23
27
|
logger = logging.getLogger(__name__)
|
|
24
28
|
|
|
@@ -330,67 +334,6 @@ ASCII_GLYPHS = Glyphs(
|
|
|
330
334
|
_glyphs_cache: Glyphs | None = None
|
|
331
335
|
"""Module-level cache for detected glyphs."""
|
|
332
336
|
|
|
333
|
-
_editable_cache: tuple[bool, str | None] | None = None
|
|
334
|
-
"""Module-level cache for editable install info: (is_editable, source_path)."""
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
def _resolve_editable_info() -> tuple[bool, str | None]:
|
|
338
|
-
"""Parse PEP 610 `direct_url.json` once and cache both results.
|
|
339
|
-
|
|
340
|
-
Returns:
|
|
341
|
-
Tuple of (is_editable, contracted_source_path). The path is
|
|
342
|
-
`~`-contracted when it falls under the user's home directory, or
|
|
343
|
-
`None` when the install is non-editable or the path is unavailable.
|
|
344
|
-
"""
|
|
345
|
-
global _editable_cache # noqa: PLW0603 # Module-level cache requires global statement
|
|
346
|
-
if _editable_cache is not None:
|
|
347
|
-
return _editable_cache
|
|
348
|
-
|
|
349
|
-
editable = False
|
|
350
|
-
path: str | None = None
|
|
351
|
-
|
|
352
|
-
try:
|
|
353
|
-
dist = distribution("Soothe")
|
|
354
|
-
raw = dist.read_text("direct_url.json")
|
|
355
|
-
if raw:
|
|
356
|
-
data = json.loads(raw)
|
|
357
|
-
editable = data.get("dir_info", {}).get("editable", False)
|
|
358
|
-
if editable:
|
|
359
|
-
url = data.get("url", "")
|
|
360
|
-
if url.startswith("file://"):
|
|
361
|
-
path = unquote(urlparse(url).path)
|
|
362
|
-
home = str(Path.home())
|
|
363
|
-
if path.startswith(home):
|
|
364
|
-
path = "~" + path[len(home) :]
|
|
365
|
-
except (PackageNotFoundError, FileNotFoundError, json.JSONDecodeError, TypeError):
|
|
366
|
-
logger.debug(
|
|
367
|
-
"Failed to read editable install info from PEP 610 metadata",
|
|
368
|
-
exc_info=True,
|
|
369
|
-
)
|
|
370
|
-
|
|
371
|
-
_editable_cache = (editable, path)
|
|
372
|
-
return _editable_cache
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
def _is_editable_install() -> bool:
|
|
376
|
-
"""Check if Soothe is installed in editable mode.
|
|
377
|
-
|
|
378
|
-
Uses PEP 610 `direct_url.json` metadata to detect editable installs.
|
|
379
|
-
|
|
380
|
-
Returns:
|
|
381
|
-
`True` if installed in editable mode, `False` otherwise.
|
|
382
|
-
"""
|
|
383
|
-
return _resolve_editable_info()[0]
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
def _get_editable_install_path() -> str | None:
|
|
387
|
-
"""Return the `~`-contracted source directory for an editable install.
|
|
388
|
-
|
|
389
|
-
Returns `None` for non-editable installs or when the path cannot be
|
|
390
|
-
determined.
|
|
391
|
-
"""
|
|
392
|
-
return _resolve_editable_info()[1]
|
|
393
|
-
|
|
394
337
|
|
|
395
338
|
def _detect_charset_mode() -> CharsetMode:
|
|
396
339
|
"""Auto-detect terminal charset capabilities.
|
|
@@ -544,19 +487,10 @@ def build_stream_config(
|
|
|
544
487
|
) -> RunnableConfig:
|
|
545
488
|
"""Build the LangGraph stream config dict.
|
|
546
489
|
|
|
547
|
-
Injects
|
|
548
|
-
correlated with specific releases.
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
* `create_deep_agent` bakes `versions: {"Soothe": "X.Y.Z"}` into the
|
|
553
|
-
compiled graph via `with_config`. At stream time, LangGraph merges
|
|
554
|
-
the graph config with the runtime config passed here. Because the
|
|
555
|
-
metadata merge is shallow (effectively `{**graph_meta, **runtime_meta}`
|
|
556
|
-
for top-level keys), both configs containing a `versions` key means
|
|
557
|
-
the runtime dict **replaces** the graph dict entirely — the SDK
|
|
558
|
-
version would be lost.
|
|
559
|
-
* Including the SDK version here ensures it survives the merge.
|
|
490
|
+
Injects the resolved Soothe version into ``metadata["versions"]`` so runs
|
|
491
|
+
can be correlated with specific releases. The runtime config replaces the
|
|
492
|
+
graph config's ``versions`` key at stream time, so this must carry the
|
|
493
|
+
canonical release string.
|
|
560
494
|
|
|
561
495
|
Args:
|
|
562
496
|
loop_id: Active StrangeLoop id (stored under LangGraph ``configurable.thread_id``).
|
|
@@ -571,8 +505,6 @@ def build_stream_config(
|
|
|
571
505
|
Returns:
|
|
572
506
|
Config dict with `configurable` and `metadata` keys.
|
|
573
507
|
"""
|
|
574
|
-
import contextlib
|
|
575
|
-
import importlib.metadata as importlib_metadata
|
|
576
508
|
from datetime import UTC, datetime
|
|
577
509
|
|
|
578
510
|
try:
|
|
@@ -581,13 +513,8 @@ def build_stream_config(
|
|
|
581
513
|
logger.warning("Could not determine working directory", exc_info=True)
|
|
582
514
|
cwd = ""
|
|
583
515
|
|
|
584
|
-
# Include SDK version alongside CLI version — see docstring for why.
|
|
585
|
-
versions: dict[str, str] = {"Soothe": __version__}
|
|
586
|
-
with contextlib.suppress(importlib_metadata.PackageNotFoundError):
|
|
587
|
-
versions["Soothe"] = importlib_metadata.version("Soothe")
|
|
588
|
-
|
|
589
516
|
metadata: dict[str, Any] = {
|
|
590
|
-
"versions":
|
|
517
|
+
"versions": {"Soothe": __version__},
|
|
591
518
|
}
|
|
592
519
|
from soothe_cli.tui._env_vars import USER_ID
|
|
593
520
|
|
|
@@ -8,7 +8,7 @@ from typing import TYPE_CHECKING
|
|
|
8
8
|
from textual.content import Content
|
|
9
9
|
from textual.widgets import Static
|
|
10
10
|
|
|
11
|
-
from soothe_cli.runtime.presentation.duration_format import
|
|
11
|
+
from soothe_cli.runtime.presentation.duration_format import format_running_elapsed
|
|
12
12
|
from soothe_cli.tui import theme
|
|
13
13
|
from soothe_cli.tui.config import get_glyphs
|
|
14
14
|
|
|
@@ -101,7 +101,7 @@ class LoadingWidget(Static):
|
|
|
101
101
|
return f" {status}... "
|
|
102
102
|
|
|
103
103
|
def _format_hint_line(self, elapsed_secs: float, *, include_interrupt: bool = False) -> str:
|
|
104
|
-
duration =
|
|
104
|
+
duration = format_running_elapsed(elapsed_secs)
|
|
105
105
|
segments: list[str] = []
|
|
106
106
|
if self._hint_extra:
|
|
107
107
|
segments.append(self._hint_extra)
|
|
@@ -121,7 +121,7 @@ class LoadingWidget(Static):
|
|
|
121
121
|
status_part = Content.styled(self._format_status_line(self._status), colors.primary)
|
|
122
122
|
if self._paused:
|
|
123
123
|
spinner_part = Content.styled(get_glyphs().pause, "dim")
|
|
124
|
-
paused_duration =
|
|
124
|
+
paused_duration = format_running_elapsed(float(self._paused_total_elapsed))
|
|
125
125
|
if self._show_interrupt_hint:
|
|
126
126
|
paused_hint = f" (paused at {paused_duration} · esc to interrupt)"
|
|
127
127
|
else:
|
{soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/widgets/messages/cognition_goal_tree.py
RENAMED
|
@@ -11,7 +11,7 @@ from textual.containers import Vertical
|
|
|
11
11
|
from textual.content import Content
|
|
12
12
|
from textual.widgets import Static
|
|
13
13
|
|
|
14
|
-
from soothe_cli.runtime.presentation.duration_format import format_duration
|
|
14
|
+
from soothe_cli.runtime.presentation.duration_format import format_duration, format_running_elapsed
|
|
15
15
|
from soothe_cli.tui import theme
|
|
16
16
|
from soothe_cli.tui.config import get_glyphs
|
|
17
17
|
from soothe_cli.tui.preview_limits import PLAN_QUICK_VIEW_STEP_LINE_MAX_CHARS
|
|
@@ -253,8 +253,7 @@ class CognitionGoalTreeMessage(Vertical):
|
|
|
253
253
|
if st.phase == "running":
|
|
254
254
|
parts: list[str] = []
|
|
255
255
|
if st.started_at is not None:
|
|
256
|
-
|
|
257
|
-
parts.append(format_duration(elapsed_s))
|
|
256
|
+
parts.append(format_running_elapsed(time() - st.started_at))
|
|
258
257
|
if st.tool_call_count > 0:
|
|
259
258
|
parts.append(f"{st.tool_call_count} tools")
|
|
260
259
|
if not parts:
|
|
@@ -19,7 +19,7 @@ from soothe_sdk.ux.task_namespace import (
|
|
|
19
19
|
)
|
|
20
20
|
from textual.content import Content
|
|
21
21
|
|
|
22
|
-
from soothe_cli.runtime.presentation.duration_format import
|
|
22
|
+
from soothe_cli.runtime.presentation.duration_format import format_running_elapsed
|
|
23
23
|
from soothe_cli.tui import theme
|
|
24
24
|
from soothe_cli.tui.commands.subagent_routing import get_subagent_display_name
|
|
25
25
|
from soothe_cli.tui.preview_limits import STEP_CARD_TOOL_ACTIVITY_PREVIEW_COUNT
|
|
@@ -433,7 +433,7 @@ class StepCardStatusLine:
|
|
|
433
433
|
"""Step card footer while executing."""
|
|
434
434
|
elapsed = ""
|
|
435
435
|
if elapsed_secs is not None:
|
|
436
|
-
elapsed = f" ({
|
|
436
|
+
elapsed = f" ({format_running_elapsed(float(elapsed_secs))})"
|
|
437
437
|
head = f"{gutter}{spinner_frame} Running{retry_suffix}...{elapsed}"
|
|
438
438
|
tail = f"{stats_suffix}{token_suffix}"
|
|
439
439
|
parts: list[object] = [Content.styled(head, colors.warning)]
|
{soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/widgets/plan_quick_view_overlay.py
RENAMED
|
@@ -108,6 +108,7 @@ class PlanQuickViewOverlay(Vertical):
|
|
|
108
108
|
|
|
109
109
|
def on_mount(self) -> None:
|
|
110
110
|
self._content = self.query_one("#plan-quick-view-content", Static)
|
|
111
|
+
self.query_one("#plan-quick-view-body", VerticalScroll).can_focus = False
|
|
111
112
|
self.display = False
|
|
112
113
|
|
|
113
114
|
@property
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
"""Version information and lightweight constants for `soothe`."""
|
|
2
|
-
|
|
3
|
-
from importlib.metadata import PackageNotFoundError, version
|
|
4
|
-
|
|
5
|
-
try:
|
|
6
|
-
__version__ = version("soothe")
|
|
7
|
-
except PackageNotFoundError:
|
|
8
|
-
# Fallback for development/editable installs
|
|
9
|
-
__version__ = "0.0.0"
|
|
10
|
-
|
|
11
|
-
DOCS_URL = "https://github.com/mirasoth/soothe/docs"
|
|
12
|
-
"""URL for Soothe documentation."""
|
|
13
|
-
|
|
14
|
-
PYPI_URL = "https://pypi.org/pypi/soothe/json"
|
|
15
|
-
"""PyPI JSON API endpoint for version checks."""
|
|
16
|
-
|
|
17
|
-
CHANGELOG_URL = "https://github.com/mirasoth/soothe/blob/main/CHANGELOG.md"
|
|
18
|
-
"""URL for the full changelog."""
|
|
19
|
-
|
|
20
|
-
USER_AGENT = f"soothe/{__version__} update-check"
|
|
21
|
-
"""User-Agent header sent with PyPI requests."""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/runtime/parse/tool_call_resolution.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/runtime/presentation/renderer_base.py
RENAMED
|
File without changes
|
{soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/runtime/presentation/renderer_protocol.py
RENAMED
|
File without changes
|
{soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/runtime/presentation/subagent_task_display.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/widgets/messages/clarification.py
RENAMED
|
File without changes
|
{soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/widgets/messages/cognition_reason.py
RENAMED
|
File without changes
|
{soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/widgets/messages/cognition_step.py
RENAMED
|
File without changes
|
{soothe_cli-0.7.11 → soothe_cli-0.7.12}/src/soothe_cli/tui/widgets/messages/cognition_subagent.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|