soothe-cli 0.7.5__tar.gz → 0.7.6__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.5 → soothe_cli-0.7.6}/PKG-INFO +1 -1
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/runtime/state/file_tracker.py +58 -5
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/app/app.tcss +0 -5
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/binding.py +1 -1
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/file_change_notify.py +11 -3
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/file_change_renderers.py +70 -12
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/textual_adapter.py +24 -1
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/theme.py +3 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/widgets/file_change_preview.py +22 -18
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/widgets/messages/cognition_step.py +1 -2
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/widgets/messages/cognition_step_activity.py +21 -14
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/widgets/status.py +1 -1
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/widgets/welcome.py +1 -1
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/.gitignore +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/README.md +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/pyproject.toml +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/__init__.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/cli/__init__.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/cli/commands/__init__.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/cli/commands/autopilot_cmd.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/cli/commands/config_cmd.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/cli/commands/cron_cmd.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/cli/commands/loop_cmd.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/cli/commands/run_cmd.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/cli/commands/status_cmd.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/cli/execution/__init__.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/cli/execution/daemon.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/cli/execution/daemon_errors.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/cli/execution/headless.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/cli/execution/headless_renderer.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/cli/execution/launcher.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/cli/main.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/config/__init__.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/config/cli_config.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/config/loader.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/config/logging_setup.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/runtime/__init__.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/runtime/headless/processor.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/runtime/headless/processor_state.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/runtime/parse/message_processing.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/runtime/parse/tool_call_resolution.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/runtime/parse/tool_result.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/runtime/policy/display_policy.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/runtime/policy/essential_events.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/runtime/policy/tui_trace_log.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/runtime/presentation/duration_format.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/runtime/presentation/engine.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/runtime/presentation/renderer_base.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/runtime/presentation/renderer_protocol.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/runtime/presentation/subagent_task_display.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/runtime/state/session_stats.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/runtime/state/step_router.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/runtime/state/stream_accumulator.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/runtime/state/transcript.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/runtime/transport/session.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/runtime/turn/pipeline.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/runtime/turn/prepare.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/runtime/wire/chunk_filter.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/runtime/wire/display_text.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/runtime/wire/message_text.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/runtime/wire/messages.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/__init__.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/_cli_context.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/_env_vars.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/_version.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/app/__init__.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/app/_app.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/app/_commands.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/app/_execution.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/app/_history.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/app/_messages_mixin.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/app/_model.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/app/_module_init.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/app/_startup.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/app/_ui.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/command_registry.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/commands/__init__.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/commands/command_router.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/commands/slash_commands.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/commands/subagent_routing.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/config.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/hooks.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/input.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/markdown_theme.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/media_utils.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/model_config.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/path_utils.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/preview_limits.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/project_utils.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/sessions.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/shell_color.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/skills/__init__.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/skills/invocation.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/skills/load.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/spinner_labels.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/tips.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/tool_display.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/unicode_security.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/update_check.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/widgets/__init__.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/widgets/_links.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/widgets/autocomplete.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/widgets/autopilot_dashboard.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/widgets/chat_input.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/widgets/clipboard.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/widgets/context_viewer.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/widgets/diff.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/widgets/editor.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/widgets/help_screen.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/widgets/history.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/widgets/loading.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/widgets/loop_selector.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/widgets/mcp_viewer.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/widgets/message_store.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/widgets/messages/__init__.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/widgets/messages/_helpers.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/widgets/messages/app.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/widgets/messages/assistant.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/widgets/messages/clarification.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/widgets/messages/cognition_goal_tree.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/widgets/messages/cognition_reason.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/widgets/messages/cognition_subagent.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/widgets/messages/diff_message.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/widgets/messages/error.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/widgets/messages/skill.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/widgets/messages/user.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/widgets/model_selector.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/widgets/notification_settings.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/widgets/plan_quick_view_overlay.py +0 -0
- {soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/widgets/theme_selector.py +0 -0
|
@@ -8,17 +8,27 @@ from dataclasses import dataclass, field
|
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
from typing import Any, Literal
|
|
10
10
|
|
|
11
|
+
from soothe_sdk.tools.metadata import get_file_write_tool_names
|
|
12
|
+
|
|
11
13
|
from soothe_cli.tui.preview_limits import APPROVAL_DIFF_MAX_LINES
|
|
12
14
|
|
|
13
15
|
logger = logging.getLogger(__name__)
|
|
14
16
|
|
|
15
17
|
FileOpStatus = Literal["pending", "success", "error"]
|
|
16
18
|
|
|
17
|
-
FILE_CHANGE_TOOLS: frozenset[str] =
|
|
18
|
-
{"write_file", "edit_file", "edit_file_lines", "delete_file"}
|
|
19
|
-
)
|
|
19
|
+
FILE_CHANGE_TOOLS: frozenset[str] = get_file_write_tool_names()
|
|
20
20
|
"""Filesystem tools that produce before/after diffs in the TUI chat."""
|
|
21
21
|
|
|
22
|
+
_FILE_CHANGE_PREVIEW_LABELS: dict[str, str] = {
|
|
23
|
+
"write_file": "Writing",
|
|
24
|
+
"edit_file": "Editing",
|
|
25
|
+
"edit_file_lines": "Editing",
|
|
26
|
+
"insert_lines": "Inserting",
|
|
27
|
+
"delete_lines": "Deleting",
|
|
28
|
+
"apply_diff": "Patching",
|
|
29
|
+
"delete_file": "Deleting",
|
|
30
|
+
}
|
|
31
|
+
|
|
22
32
|
|
|
23
33
|
def _safe_read(path: Path) -> str | None:
|
|
24
34
|
"""Read file content, returning None on failure.
|
|
@@ -150,6 +160,22 @@ def resolve_physical_path(path_str: str | None, assistant_id: str | None) -> Pat
|
|
|
150
160
|
return None
|
|
151
161
|
|
|
152
162
|
|
|
163
|
+
def parse_insert_line_arg(args: dict[str, Any]) -> int | None:
|
|
164
|
+
"""Parse ``line`` from insert_lines tool args (1-indexed).
|
|
165
|
+
|
|
166
|
+
Returns:
|
|
167
|
+
Line number or None when missing or not an integer.
|
|
168
|
+
"""
|
|
169
|
+
line = args.get("line")
|
|
170
|
+
if isinstance(line, bool):
|
|
171
|
+
return None
|
|
172
|
+
if isinstance(line, int):
|
|
173
|
+
return line
|
|
174
|
+
if isinstance(line, float) and line.is_integer():
|
|
175
|
+
return int(line)
|
|
176
|
+
return None
|
|
177
|
+
|
|
178
|
+
|
|
153
179
|
def parse_line_range_args(args: dict[str, Any]) -> tuple[int, int] | None:
|
|
154
180
|
"""Parse ``start_line`` and ``end_line`` from tool args (1-indexed inclusive).
|
|
155
181
|
|
|
@@ -188,6 +214,26 @@ def extract_line_range_text(content: str, start_line: int, end_line: int) -> str
|
|
|
188
214
|
return "".join(lines[start_line - 1 : end_line])
|
|
189
215
|
|
|
190
216
|
|
|
217
|
+
def apply_insert_lines_to_content(content: str, line: int, insert_content: str) -> str | None:
|
|
218
|
+
"""Insert ``insert_content`` before line ``line`` (matches middleware semantics).
|
|
219
|
+
|
|
220
|
+
Returns:
|
|
221
|
+
Modified file text, or None when ``line`` is out of range for ``content``.
|
|
222
|
+
"""
|
|
223
|
+
lines = content.splitlines(keepends=True)
|
|
224
|
+
total = len(lines)
|
|
225
|
+
if total == 0 and content:
|
|
226
|
+
lines = [content]
|
|
227
|
+
total = 1
|
|
228
|
+
if line < 1 or line > total + 1:
|
|
229
|
+
return None
|
|
230
|
+
new_lines = insert_content.splitlines(keepends=True)
|
|
231
|
+
if new_lines and not new_lines[-1].endswith("\n"):
|
|
232
|
+
new_lines[-1] += "\n"
|
|
233
|
+
lines[line - 1 : line - 1] = new_lines
|
|
234
|
+
return "".join(lines)
|
|
235
|
+
|
|
236
|
+
|
|
191
237
|
def apply_edit_file_lines_to_content(
|
|
192
238
|
content: str,
|
|
193
239
|
start_line: int,
|
|
@@ -393,14 +439,21 @@ def track_file_operation(
|
|
|
393
439
|
tracker.start_operation(tool_name, args, tcid)
|
|
394
440
|
|
|
395
441
|
|
|
442
|
+
def file_change_preview_label(tool_name: str) -> str:
|
|
443
|
+
"""Short header label for a pending filesystem tool preview."""
|
|
444
|
+
return _FILE_CHANGE_PREVIEW_LABELS.get(tool_name, "Change")
|
|
445
|
+
|
|
446
|
+
|
|
396
447
|
def file_change_action_label(record: FileOperationRecord) -> str:
|
|
397
448
|
"""Human-readable label for a completed file operation (chat diff header)."""
|
|
398
449
|
if record.tool_name == "delete_file":
|
|
399
450
|
return "Deleted"
|
|
400
451
|
if record.tool_name == "write_file" and not (record.before_content or ""):
|
|
401
|
-
return "
|
|
452
|
+
return "Created"
|
|
402
453
|
if record.tool_name == "write_file":
|
|
403
454
|
return "Written"
|
|
404
|
-
if record.tool_name in ("edit_file", "edit_file_lines"):
|
|
455
|
+
if record.tool_name in ("edit_file", "edit_file_lines", "delete_lines", "apply_diff"):
|
|
405
456
|
return "Updated"
|
|
457
|
+
if record.tool_name == "insert_lines":
|
|
458
|
+
return "Inserted"
|
|
406
459
|
return "Changed"
|
|
@@ -274,7 +274,7 @@ def message_from_widget(widget: Widget) -> MessageData:
|
|
|
274
274
|
if isinstance(widget, FileChangePreviewWidget):
|
|
275
275
|
path = str(widget.data.get("file_path") or widget.data.get("path") or "")
|
|
276
276
|
summary = f"{widget._action_label}: {path}" if path else widget._action_label
|
|
277
|
-
return MessageData(type=MessageType.APP, content=summary or "
|
|
277
|
+
return MessageData(type=MessageType.APP, content=summary or "Change", id=widget_id)
|
|
278
278
|
|
|
279
279
|
if isinstance(widget, SummarizationMessage):
|
|
280
280
|
return MessageData(
|
|
@@ -6,11 +6,13 @@ import asyncio
|
|
|
6
6
|
import logging
|
|
7
7
|
from typing import TYPE_CHECKING, Any
|
|
8
8
|
|
|
9
|
-
from soothe_cli.runtime.state.file_tracker import
|
|
10
|
-
|
|
11
|
-
build_file_change_preview,
|
|
9
|
+
from soothe_cli.runtime.state.file_tracker import (
|
|
10
|
+
FILE_CHANGE_TOOLS,
|
|
12
11
|
file_change_preview_label,
|
|
12
|
+
parse_insert_line_arg,
|
|
13
|
+
parse_line_range_args,
|
|
13
14
|
)
|
|
15
|
+
from soothe_cli.tui.file_change_renderers import build_file_change_preview
|
|
14
16
|
|
|
15
17
|
if TYPE_CHECKING:
|
|
16
18
|
from soothe_cli.runtime.state.file_tracker import FileOperationRecord
|
|
@@ -76,6 +78,12 @@ async def mount_file_change_preview(
|
|
|
76
78
|
return
|
|
77
79
|
if tool_name == "edit_file_lines" and parse_line_range_args(args) is None:
|
|
78
80
|
return
|
|
81
|
+
if tool_name == "insert_lines" and parse_insert_line_arg(args) is None:
|
|
82
|
+
return
|
|
83
|
+
if tool_name == "delete_lines" and parse_line_range_args(args) is None:
|
|
84
|
+
return
|
|
85
|
+
if tool_name == "apply_diff" and not str(args.get("diff") or "").strip():
|
|
86
|
+
return
|
|
79
87
|
|
|
80
88
|
built = build_file_change_preview(tool_name, args, assistant_id=assistant_id)
|
|
81
89
|
if built is None:
|
|
@@ -7,7 +7,10 @@ from typing import Any
|
|
|
7
7
|
from soothe_cli.runtime.state.file_tracker import (
|
|
8
8
|
FILE_CHANGE_TOOLS,
|
|
9
9
|
FileOperationRecord,
|
|
10
|
+
apply_edit_file_lines_to_content,
|
|
11
|
+
apply_insert_lines_to_content,
|
|
10
12
|
extract_line_range_text,
|
|
13
|
+
parse_insert_line_arg,
|
|
11
14
|
parse_line_range_args,
|
|
12
15
|
read_physical_file_text,
|
|
13
16
|
resolve_physical_path,
|
|
@@ -19,22 +22,11 @@ from soothe_cli.tui.widgets.file_change_preview import (
|
|
|
19
22
|
EditFilePreviewWidget,
|
|
20
23
|
FileChangePreviewWidget,
|
|
21
24
|
GenericFilePreviewWidget,
|
|
25
|
+
InsertLinesPreviewWidget,
|
|
22
26
|
WriteFilePreviewWidget,
|
|
23
27
|
unified_diff_body_lines,
|
|
24
28
|
)
|
|
25
29
|
|
|
26
|
-
_PREVIEW_LABELS: dict[str, str] = {
|
|
27
|
-
"write_file": "Writing file",
|
|
28
|
-
"edit_file": "Editing file",
|
|
29
|
-
"edit_file_lines": "Editing lines",
|
|
30
|
-
"delete_file": "Deleting file",
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
def file_change_preview_label(tool_name: str) -> str:
|
|
35
|
-
"""Short header label for a pending filesystem tool preview."""
|
|
36
|
-
return _PREVIEW_LABELS.get(tool_name, "File change")
|
|
37
|
-
|
|
38
30
|
|
|
39
31
|
def build_file_change_preview(
|
|
40
32
|
tool_name: str,
|
|
@@ -109,6 +101,62 @@ def build_file_change_preview(
|
|
|
109
101
|
"new_string": new_string,
|
|
110
102
|
}
|
|
111
103
|
|
|
104
|
+
if tool_name == "insert_lines":
|
|
105
|
+
line = parse_insert_line_arg(args)
|
|
106
|
+
if line is None:
|
|
107
|
+
return GenericFilePreviewWidget, dict(args)
|
|
108
|
+
insert_content = str(args.get("content") or "")
|
|
109
|
+
before = ""
|
|
110
|
+
physical = resolve_physical_path(path_str, assistant_id)
|
|
111
|
+
if physical and physical.is_file():
|
|
112
|
+
before = read_physical_file_text(physical) or ""
|
|
113
|
+
after_text = apply_insert_lines_to_content(before, line, insert_content) or before
|
|
114
|
+
return InsertLinesPreviewWidget, {
|
|
115
|
+
"file_path": path_str,
|
|
116
|
+
"insert_line": line,
|
|
117
|
+
"diff_lines": unified_diff_body_lines(before, after_text),
|
|
118
|
+
"old_string": before,
|
|
119
|
+
"new_string": after_text,
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if tool_name == "delete_lines":
|
|
123
|
+
line_range = parse_line_range_args(args)
|
|
124
|
+
if line_range is None:
|
|
125
|
+
return GenericFilePreviewWidget, dict(args)
|
|
126
|
+
start_line, end_line = line_range
|
|
127
|
+
before = ""
|
|
128
|
+
physical = resolve_physical_path(path_str, assistant_id)
|
|
129
|
+
if physical and physical.is_file():
|
|
130
|
+
before = read_physical_file_text(physical) or ""
|
|
131
|
+
old_segment = extract_line_range_text(before, start_line, end_line) if before else ""
|
|
132
|
+
after_text = (
|
|
133
|
+
apply_edit_file_lines_to_content(before, start_line, end_line, "") if before else ""
|
|
134
|
+
)
|
|
135
|
+
if after_text is None:
|
|
136
|
+
after_text = before
|
|
137
|
+
return EditFileLinesPreviewWidget, {
|
|
138
|
+
"file_path": path_str,
|
|
139
|
+
"start_line": start_line,
|
|
140
|
+
"end_line": end_line,
|
|
141
|
+
"diff_lines": unified_diff_body_lines(before, after_text),
|
|
142
|
+
"old_string": old_segment,
|
|
143
|
+
"new_string": "",
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if tool_name == "apply_diff":
|
|
147
|
+
diff_text = str(args.get("diff") or "")
|
|
148
|
+
before = ""
|
|
149
|
+
physical = resolve_physical_path(path_str, assistant_id)
|
|
150
|
+
if physical and physical.is_file():
|
|
151
|
+
before = read_physical_file_text(physical) or ""
|
|
152
|
+
diff_lines = [ln for ln in diff_text.splitlines() if ln.strip()]
|
|
153
|
+
return EditFilePreviewWidget, {
|
|
154
|
+
"file_path": path_str,
|
|
155
|
+
"diff_lines": diff_lines,
|
|
156
|
+
"old_string": before,
|
|
157
|
+
"new_string": "",
|
|
158
|
+
}
|
|
159
|
+
|
|
112
160
|
if tool_name == "delete_file":
|
|
113
161
|
content = ""
|
|
114
162
|
physical = resolve_physical_path(path_str, assistant_id)
|
|
@@ -161,6 +209,16 @@ def update_preview_data_from_record(data: dict[str, Any], record: FileOperationR
|
|
|
161
209
|
if line_range is not None:
|
|
162
210
|
data["start_line"], data["end_line"] = line_range
|
|
163
211
|
|
|
212
|
+
if record.tool_name == "insert_lines":
|
|
213
|
+
line = parse_insert_line_arg(record.args)
|
|
214
|
+
if line is not None:
|
|
215
|
+
data["insert_line"] = line
|
|
216
|
+
|
|
217
|
+
if record.tool_name == "delete_lines":
|
|
218
|
+
line_range = parse_line_range_args(record.args)
|
|
219
|
+
if line_range is not None:
|
|
220
|
+
data["start_line"], data["end_line"] = line_range
|
|
221
|
+
|
|
164
222
|
if record.diff:
|
|
165
223
|
data["diff_lines"] = record.diff.splitlines()
|
|
166
224
|
data["old_string"] = record.before_content or ""
|
|
@@ -290,6 +290,9 @@ class TextualUIAdapter:
|
|
|
290
290
|
self._execute_wave_completed: int = 0
|
|
291
291
|
"""Completed steps in the current execute batch."""
|
|
292
292
|
|
|
293
|
+
self._successful_step_ids: set[str] = set()
|
|
294
|
+
"""Step ids that finished successfully this loop (survives plan=keep replays)."""
|
|
295
|
+
|
|
293
296
|
self._clarification_input_by_step: dict[str, ClarificationInputMessage] = {}
|
|
294
297
|
"""Active inline ``ClarificationInputMessage`` widgets keyed by step id.
|
|
295
298
|
|
|
@@ -1426,6 +1429,8 @@ async def sync_pending_step_cards_from_plan(
|
|
|
1426
1429
|
sid = str(row.get("id", "")).strip()
|
|
1427
1430
|
if not sid or sid in adapter._current_step_messages:
|
|
1428
1431
|
continue
|
|
1432
|
+
if _is_successful_step_id(adapter, sid):
|
|
1433
|
+
continue
|
|
1429
1434
|
desc = str(row.get("description", "")).strip() or "(step)"
|
|
1430
1435
|
step_widget = CognitionStepMessage(
|
|
1431
1436
|
step_id=sid,
|
|
@@ -1451,6 +1456,17 @@ def _step_card_lookup_keys(step_id: str) -> list[str]:
|
|
|
1451
1456
|
return keys
|
|
1452
1457
|
|
|
1453
1458
|
|
|
1459
|
+
def _register_successful_step_id(adapter: TextualUIAdapter, step_id: str) -> None:
|
|
1460
|
+
"""Remember a step that completed successfully so plan=keep does not remount it."""
|
|
1461
|
+
for key in _step_card_lookup_keys(step_id):
|
|
1462
|
+
adapter._successful_step_ids.add(key)
|
|
1463
|
+
|
|
1464
|
+
|
|
1465
|
+
def _is_successful_step_id(adapter: TextualUIAdapter, step_id: str) -> bool:
|
|
1466
|
+
"""True when this step already succeeded earlier in the current loop."""
|
|
1467
|
+
return any(key in adapter._successful_step_ids for key in _step_card_lookup_keys(step_id))
|
|
1468
|
+
|
|
1469
|
+
|
|
1454
1470
|
def _lookup_step_card(
|
|
1455
1471
|
adapter: TextualUIAdapter,
|
|
1456
1472
|
step_id: str,
|
|
@@ -2731,6 +2747,7 @@ async def execute_task_textual(
|
|
|
2731
2747
|
router.reset_turn()
|
|
2732
2748
|
adapter._execute_wave_total = 0
|
|
2733
2749
|
adapter._execute_wave_completed = 0
|
|
2750
|
+
adapter._successful_step_ids.clear()
|
|
2734
2751
|
ui_coalesce = TurnToolUiCoalescer()
|
|
2735
2752
|
adapter._goal_completion_mounted_this_turn = False
|
|
2736
2753
|
adapter._goal_tree_message = None
|
|
@@ -3742,7 +3759,11 @@ async def execute_task_textual(
|
|
|
3742
3759
|
execution_mode = str(data.get("execution_mode", "")).strip()
|
|
3743
3760
|
adapter._last_plan_execution_mode = execution_mode or None
|
|
3744
3761
|
adapter._execute_wave_total = len(raw_steps)
|
|
3745
|
-
|
|
3762
|
+
done_steps = int(data.get("done_steps", 0) or 0)
|
|
3763
|
+
adapter._execute_wave_completed = min(
|
|
3764
|
+
done_steps,
|
|
3765
|
+
len(raw_steps),
|
|
3766
|
+
)
|
|
3746
3767
|
await sync_pending_step_cards_from_plan(
|
|
3747
3768
|
adapter,
|
|
3748
3769
|
steps=raw_steps,
|
|
@@ -3896,6 +3917,8 @@ async def execute_task_textual(
|
|
|
3896
3917
|
pending_text_by_namespace[ns_key] = ""
|
|
3897
3918
|
assistant_message_by_namespace.pop(ns_key, None)
|
|
3898
3919
|
success = bool(data.get("success", True))
|
|
3920
|
+
if success:
|
|
3921
|
+
_register_successful_step_id(adapter, step_id)
|
|
3899
3922
|
duration_ms = int(data.get("duration_ms", 0))
|
|
3900
3923
|
tool_call_count = int(data.get("tool_call_count", 0))
|
|
3901
3924
|
summary = str(
|
|
@@ -38,6 +38,9 @@ if TYPE_CHECKING:
|
|
|
38
38
|
|
|
39
39
|
logger = logging.getLogger(__name__)
|
|
40
40
|
|
|
41
|
+
# Rich/Textual style for de-emphasized secondary labels (loop id, tool stats, activity).
|
|
42
|
+
SECONDARY_TEXT_STYLE: str = "dim"
|
|
43
|
+
|
|
41
44
|
# ---------------------------------------------------------------------------
|
|
42
45
|
# Brand palette — dark (originally tokyonight-inspired, LangChain blue primary)
|
|
43
46
|
# ---------------------------------------------------------------------------
|
|
@@ -125,7 +125,6 @@ class FileChangePreviewWidget(Vertical):
|
|
|
125
125
|
|
|
126
126
|
FileChangePreviewWidget.-collapsed .file-change-preview-body,
|
|
127
127
|
FileChangePreviewWidget.-collapsed .file-change-preview-section-label,
|
|
128
|
-
FileChangePreviewWidget.-collapsed .file-change-preview-label,
|
|
129
128
|
FileChangePreviewWidget.-collapsed .diff-line-added,
|
|
130
129
|
FileChangePreviewWidget.-collapsed .diff-line-removed,
|
|
131
130
|
FileChangePreviewWidget.-collapsed .diff-context {
|
|
@@ -193,15 +192,6 @@ class FileChangePreviewWidget(Vertical):
|
|
|
193
192
|
await self.mount(*self.compose())
|
|
194
193
|
self._apply_expand_classes()
|
|
195
194
|
|
|
196
|
-
def compose(self) -> ComposeResult: # noqa: PLR6301
|
|
197
|
-
"""Default compose — subclasses override."""
|
|
198
|
-
if self._action_label:
|
|
199
|
-
yield Static(
|
|
200
|
-
Content.from_markup("[bold]$label[/bold]", label=self._action_label),
|
|
201
|
-
classes="file-change-preview-label",
|
|
202
|
-
)
|
|
203
|
-
yield Static("Change details not available", classes="file-change-preview-body")
|
|
204
|
-
|
|
205
195
|
def _yield_compact_header(
|
|
206
196
|
self,
|
|
207
197
|
file_path: str,
|
|
@@ -270,7 +260,7 @@ class WriteFilePreviewWidget(FileChangePreviewWidget):
|
|
|
270
260
|
total_lines = len(lines)
|
|
271
261
|
|
|
272
262
|
if is_new_file:
|
|
273
|
-
extra = "new
|
|
263
|
+
extra = "new" if not self._finalized else ""
|
|
274
264
|
yield from self._yield_compact_header(file_path, extra=extra)
|
|
275
265
|
else:
|
|
276
266
|
yield from self._yield_compact_header(
|
|
@@ -358,9 +348,14 @@ class EditFilePreviewWidget(FileChangePreviewWidget):
|
|
|
358
348
|
|
|
359
349
|
def compose(self) -> ComposeResult:
|
|
360
350
|
"""Compose diff preview."""
|
|
361
|
-
yield from self._compose_edit_diff_body(
|
|
351
|
+
yield from self._compose_edit_diff_body()
|
|
362
352
|
|
|
363
|
-
def _compose_edit_diff_body(
|
|
353
|
+
def _compose_edit_diff_body(
|
|
354
|
+
self,
|
|
355
|
+
*,
|
|
356
|
+
show_line_range: bool = False,
|
|
357
|
+
show_insert_line: bool = False,
|
|
358
|
+
) -> ComposeResult:
|
|
364
359
|
file_path = self.data.get("file_path", "")
|
|
365
360
|
diff_lines = self.data.get("diff_lines", [])
|
|
366
361
|
old_string = self.data.get("old_string", "")
|
|
@@ -373,6 +368,10 @@ class EditFilePreviewWidget(FileChangePreviewWidget):
|
|
|
373
368
|
end_line = self.data.get("end_line")
|
|
374
369
|
if isinstance(start_line, int) and isinstance(end_line, int):
|
|
375
370
|
extra = f"lines {start_line}–{end_line}"
|
|
371
|
+
elif show_insert_line:
|
|
372
|
+
insert_line = self.data.get("insert_line")
|
|
373
|
+
if isinstance(insert_line, int):
|
|
374
|
+
extra = f"at line {insert_line}"
|
|
376
375
|
yield from self._yield_compact_header(
|
|
377
376
|
file_path,
|
|
378
377
|
additions=additions,
|
|
@@ -404,13 +403,21 @@ class EditFilePreviewWidget(FileChangePreviewWidget):
|
|
|
404
403
|
|
|
405
404
|
|
|
406
405
|
class EditFileLinesPreviewWidget(EditFilePreviewWidget):
|
|
407
|
-
"""Preview for edit_file_lines — line range + segment diff."""
|
|
406
|
+
"""Preview for edit_file_lines / delete_lines — line range + segment diff."""
|
|
408
407
|
|
|
409
408
|
def compose(self) -> ComposeResult:
|
|
410
409
|
"""Compose line-range edit preview."""
|
|
411
410
|
yield from self._compose_edit_diff_body(show_line_range=True)
|
|
412
411
|
|
|
413
412
|
|
|
413
|
+
class InsertLinesPreviewWidget(EditFilePreviewWidget):
|
|
414
|
+
"""Preview for insert_lines — insertion point + file diff."""
|
|
415
|
+
|
|
416
|
+
def compose(self) -> ComposeResult:
|
|
417
|
+
"""Compose insert-at-line preview."""
|
|
418
|
+
yield from self._compose_edit_diff_body(show_insert_line=True)
|
|
419
|
+
|
|
420
|
+
|
|
414
421
|
class GenericFilePreviewWidget(FileChangePreviewWidget):
|
|
415
422
|
"""Fallback preview — key/value args."""
|
|
416
423
|
|
|
@@ -419,10 +426,7 @@ class GenericFilePreviewWidget(FileChangePreviewWidget):
|
|
|
419
426
|
if file_path:
|
|
420
427
|
yield from self._yield_compact_header(file_path)
|
|
421
428
|
elif self._action_label:
|
|
422
|
-
yield
|
|
423
|
-
Content.from_markup("[bold]$label[/bold]", label=self._action_label),
|
|
424
|
-
classes="file-change-preview-header",
|
|
425
|
-
)
|
|
429
|
+
yield from self._yield_compact_header("")
|
|
426
430
|
for key, value in self.data.items():
|
|
427
431
|
if value is None:
|
|
428
432
|
continue
|
|
@@ -561,10 +561,9 @@ class CognitionStepMessage(Vertical):
|
|
|
561
561
|
colors = theme.DARK_COLORS
|
|
562
562
|
icon = g.checkmark if success else g.error
|
|
563
563
|
head_tone = colors.card_success if success else colors.card_error
|
|
564
|
-
suffix_tone = colors.cognition
|
|
565
564
|
parts: list[object] = [Content.styled(f"{gutter}{icon} {head}", head_tone)]
|
|
566
565
|
if suffix:
|
|
567
|
-
parts.append(Content.styled(suffix,
|
|
566
|
+
parts.append(Content.styled(suffix, theme.SECONDARY_TEXT_STYLE))
|
|
568
567
|
self._status_widget.update(Content.assemble(*parts))
|
|
569
568
|
self._status_widget.display = True
|
|
570
569
|
|
{soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/widgets/messages/cognition_step_activity.py
RENAMED
|
@@ -20,6 +20,7 @@ from soothe_sdk.ux.task_namespace import (
|
|
|
20
20
|
from textual.content import Content
|
|
21
21
|
|
|
22
22
|
from soothe_cli.runtime.presentation.duration_format import format_duration
|
|
23
|
+
from soothe_cli.tui import theme
|
|
23
24
|
from soothe_cli.tui.commands.subagent_routing import get_subagent_display_name
|
|
24
25
|
from soothe_cli.tui.preview_limits import STEP_CARD_TOOL_ACTIVITY_PREVIEW_COUNT
|
|
25
26
|
from soothe_cli.tui.tool_display import (
|
|
@@ -348,13 +349,9 @@ def phase_icon(
|
|
|
348
349
|
def task_tool_row_tone_for_phase(phase: str, colors: Any) -> str:
|
|
349
350
|
"""Textual style for a tool row or task label from lifecycle phase."""
|
|
350
351
|
p = (phase or "pending").strip().lower()
|
|
351
|
-
if p in ("success", "done"):
|
|
352
|
-
return colors.cognition
|
|
353
352
|
if p in ("error", "rejected", "failed"):
|
|
354
353
|
return colors.error
|
|
355
|
-
|
|
356
|
-
return colors.cognition
|
|
357
|
-
return colors.muted
|
|
354
|
+
return theme.SECONDARY_TEXT_STYLE
|
|
358
355
|
|
|
359
356
|
|
|
360
357
|
def task_tool_row_tone(row: StepToolRow, colors: Any) -> str:
|
|
@@ -441,7 +438,7 @@ class StepCardStatusLine:
|
|
|
441
438
|
tail = f"{stats_suffix}{token_suffix}"
|
|
442
439
|
parts: list[object] = [Content.styled(head, colors.warning)]
|
|
443
440
|
if tail:
|
|
444
|
-
parts.append(Content.styled(tail,
|
|
441
|
+
parts.append(Content.styled(tail, theme.SECONDARY_TEXT_STYLE))
|
|
445
442
|
return Content.assemble(*parts)
|
|
446
443
|
|
|
447
444
|
@staticmethod
|
|
@@ -454,8 +451,12 @@ class StepCardStatusLine:
|
|
|
454
451
|
colors: Any,
|
|
455
452
|
) -> Content:
|
|
456
453
|
"""Step card footer for planned steps not yet executing."""
|
|
457
|
-
|
|
458
|
-
|
|
454
|
+
head = f"{gutter}{circle_empty} Pending..."
|
|
455
|
+
tail = f"{stats_suffix}{token_suffix}"
|
|
456
|
+
parts: list[object] = [Content.styled(head, colors.cognition)]
|
|
457
|
+
if tail:
|
|
458
|
+
parts.append(Content.styled(tail, theme.SECONDARY_TEXT_STYLE))
|
|
459
|
+
return Content.assemble(*parts)
|
|
459
460
|
|
|
460
461
|
@staticmethod
|
|
461
462
|
def footer_queued(
|
|
@@ -467,8 +468,12 @@ class StepCardStatusLine:
|
|
|
467
468
|
colors: Any,
|
|
468
469
|
) -> Content:
|
|
469
470
|
"""Step card footer for ready steps waiting for a concurrency slot."""
|
|
470
|
-
|
|
471
|
-
|
|
471
|
+
head = f"{gutter}{circle_empty} Queued..."
|
|
472
|
+
tail = f"{stats_suffix}{token_suffix}"
|
|
473
|
+
parts: list[object] = [Content.styled(head, colors.cognition)]
|
|
474
|
+
if tail:
|
|
475
|
+
parts.append(Content.styled(tail, theme.SECONDARY_TEXT_STYLE))
|
|
476
|
+
return Content.assemble(*parts)
|
|
472
477
|
|
|
473
478
|
|
|
474
479
|
# Activity tree renderer
|
|
@@ -523,7 +528,7 @@ class StepActivityTree:
|
|
|
523
528
|
parts.append(
|
|
524
529
|
Content.styled(
|
|
525
530
|
f"{branch_gutter}{task_icon} {label}",
|
|
526
|
-
task_tone
|
|
531
|
+
task_tone,
|
|
527
532
|
)
|
|
528
533
|
)
|
|
529
534
|
|
|
@@ -533,7 +538,7 @@ class StepActivityTree:
|
|
|
533
538
|
if not text:
|
|
534
539
|
continue
|
|
535
540
|
parts.append("\n")
|
|
536
|
-
parts.append(Content.styled(f"{branch_gutter}{text}",
|
|
541
|
+
parts.append(Content.styled(f"{branch_gutter}{text}", theme.SECONDARY_TEXT_STYLE))
|
|
537
542
|
|
|
538
543
|
# Main-agent tool preview
|
|
539
544
|
if main_preview:
|
|
@@ -551,7 +556,9 @@ class StepActivityTree:
|
|
|
551
556
|
if hidden_tools > 0:
|
|
552
557
|
label = f"+{hidden_tools} more tool{'s' if hidden_tools != 1 else ''}"
|
|
553
558
|
parts.append("\n")
|
|
554
|
-
parts.append(
|
|
559
|
+
parts.append(
|
|
560
|
+
Content.styled(f"{branch_gutter}· {label}", theme.SECONDARY_TEXT_STYLE)
|
|
561
|
+
)
|
|
555
562
|
|
|
556
563
|
# Global subagent notes
|
|
557
564
|
for note in subagent_notes:
|
|
@@ -561,6 +568,6 @@ class StepActivityTree:
|
|
|
561
568
|
if not first_block:
|
|
562
569
|
parts.append("\n")
|
|
563
570
|
first_block = False
|
|
564
|
-
parts.append(Content.styled(f"{branch_gutter}{t}",
|
|
571
|
+
parts.append(Content.styled(f"{branch_gutter}{t}", theme.SECONDARY_TEXT_STYLE))
|
|
565
572
|
|
|
566
573
|
return Content.assemble(*parts) if parts else Content("")
|
|
@@ -297,7 +297,7 @@ class StatusBar(Horizontal):
|
|
|
297
297
|
except NoMatches:
|
|
298
298
|
return
|
|
299
299
|
text = (new_value or "").strip()
|
|
300
|
-
tip_widget.update(f"
|
|
300
|
+
tip_widget.update(f"Tip: {text}" if text else "")
|
|
301
301
|
|
|
302
302
|
def watch_cwd(self, new_value: str) -> None:
|
|
303
303
|
"""Update cwd display when it changes."""
|
|
@@ -144,7 +144,7 @@ class WelcomeBanner(Static):
|
|
|
144
144
|
def _build_metadata_line(self, *, colors: theme.ThemeColors, ansi: bool) -> Content | None:
|
|
145
145
|
"""Build the single metadata row: loop id plus version or update notice."""
|
|
146
146
|
segments: list[str | tuple[str, str | TStyle]] = []
|
|
147
|
-
dim_style: str | TStyle =
|
|
147
|
+
dim_style: str | TStyle = theme.SECONDARY_TEXT_STYLE
|
|
148
148
|
|
|
149
149
|
if self._cli_loop_id:
|
|
150
150
|
segments.append((f"Loop: {self._cli_loop_id}", dim_style))
|
|
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.5 → soothe_cli-0.7.6}/src/soothe_cli/runtime/presentation/duration_format.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/runtime/presentation/renderer_protocol.py
RENAMED
|
File without changes
|
{soothe_cli-0.7.5 → soothe_cli-0.7.6}/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
|
|
File without changes
|
{soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/widgets/messages/cognition_goal_tree.py
RENAMED
|
File without changes
|
{soothe_cli-0.7.5 → soothe_cli-0.7.6}/src/soothe_cli/tui/widgets/messages/cognition_reason.py
RENAMED
|
File without changes
|
{soothe_cli-0.7.5 → soothe_cli-0.7.6}/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
|