soothe-cli 0.4.0__tar.gz → 0.4.1__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.4.0 → soothe_cli-0.4.1}/PKG-INFO +1 -1
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/cli/renderer.py +2 -2
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/cli/stream/formatter.py +15 -15
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/cli/stream/pipeline.py +10 -3
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/shared/event_processor.py +1 -1
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/shared/message_processing.py +11 -1
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/shared/tool_formatters/__init__.py +2 -0
- soothe_cli-0.4.1/src/soothe_cli/shared/tool_formatters/subagent.py +122 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/shared/tool_output_formatter.py +4 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/tool_display.py +2 -2
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/.gitignore +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/README.md +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/pyproject.toml +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/__init__.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/cli/__init__.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/cli/commands/__init__.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/cli/commands/autopilot_cmd.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/cli/commands/config_cmd.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/cli/commands/run_cmd.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/cli/commands/status_cmd.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/cli/commands/subagent_names.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/cli/commands/thread_cmd.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/cli/execution/__init__.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/cli/execution/daemon.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/cli/execution/headless.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/cli/execution/launcher.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/cli/main.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/cli/stream/__init__.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/cli/stream/context.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/cli/stream/display_line.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/cli/utils.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/config/__init__.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/config/cli_config.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/loop_commands.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/plan/__init__.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/plan/rich_tree.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/shared/__init__.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/shared/command_router.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/shared/config_loader.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/shared/display_policy.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/shared/essential_events.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/shared/presentation_engine.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/shared/processor_state.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/shared/renderer_protocol.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/shared/rendering.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/shared/slash_commands.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/shared/subagent_routing.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/shared/suppression_state.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/shared/tool_call_resolution.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/shared/tool_card_payload.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/shared/tool_formatters/base.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/shared/tool_formatters/execution.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/shared/tool_formatters/fallback.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/shared/tool_formatters/file_ops.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/shared/tool_formatters/goal_formatter.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/shared/tool_formatters/media.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/shared/tool_formatters/structured.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/shared/tool_formatters/web.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/shared/tool_message_format.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/shared/tui_trace_log.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/__init__.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/_ask_user_types.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/_cli_context.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/_env_vars.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/_session_stats.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/_version.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/app.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/app.tcss +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/command_registry.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/config.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/daemon_session.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/file_ops.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/formatting.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/hooks.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/input.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/media_utils.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/message_display_filter.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/model_config.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/output.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/preview_limits.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/project_utils.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/sessions.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/skills/__init__.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/skills/invocation.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/skills/load.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/textual_adapter.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/theme.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/unicode_security.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/update_check.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/widgets/__init__.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/widgets/_links.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/widgets/approval.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/widgets/ask_user.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/widgets/autocomplete.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/widgets/autopilot_dashboard.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/widgets/autopilot_screen.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/widgets/chat_input.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/widgets/clipboard.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/widgets/diff.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/widgets/editor.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/widgets/history.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/widgets/loading.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/widgets/loop_selector.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/widgets/mcp_viewer.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/widgets/message_store.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/widgets/messages.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/widgets/model_selector.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/widgets/notification_settings.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/widgets/status.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/widgets/theme_selector.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/widgets/thread_selector.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/widgets/tool_renderers.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/widgets/tool_widgets.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/widgets/tools.py +0 -0
- {soothe_cli-0.4.0 → soothe_cli-0.4.1}/src/soothe_cli/tui/widgets/welcome.py +0 -0
|
@@ -13,7 +13,7 @@ from dataclasses import dataclass, field
|
|
|
13
13
|
from typing import TYPE_CHECKING, Any
|
|
14
14
|
|
|
15
15
|
from soothe_sdk.core.verbosity import VerbosityTier
|
|
16
|
-
from soothe_sdk.utils import
|
|
16
|
+
from soothe_sdk.utils import get_tool_display_name
|
|
17
17
|
|
|
18
18
|
from soothe_cli.cli.stream import DisplayLine, StreamDisplayPipeline
|
|
19
19
|
from soothe_cli.cli.utils import make_tool_block
|
|
@@ -223,7 +223,7 @@ class CliRenderer:
|
|
|
223
223
|
|
|
224
224
|
self._stderr_begin_icon_block()
|
|
225
225
|
|
|
226
|
-
display_name =
|
|
226
|
+
display_name = get_tool_display_name(name)
|
|
227
227
|
|
|
228
228
|
# Pass args directly, including any _raw fallback
|
|
229
229
|
args_str = format_tool_call_args(name, {"args": args, "_raw": args.get("_raw", "")})
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
from soothe_sdk.core.verbosity import VerbosityTier
|
|
6
|
-
from soothe_sdk.utils import
|
|
6
|
+
from soothe_sdk.utils import get_tool_display_name
|
|
7
7
|
|
|
8
8
|
from soothe_cli.cli.stream.display_line import DisplayLine, indent_for_level
|
|
9
9
|
|
|
@@ -96,7 +96,7 @@ def format_goal_header(
|
|
|
96
96
|
DisplayLine for goal header.
|
|
97
97
|
"""
|
|
98
98
|
# Add inline symbol for goal marker
|
|
99
|
-
content = f"
|
|
99
|
+
content = f"{goal}"
|
|
100
100
|
return DisplayLine(
|
|
101
101
|
level=1,
|
|
102
102
|
content=content,
|
|
@@ -126,7 +126,7 @@ def format_step_header(
|
|
|
126
126
|
"""
|
|
127
127
|
suffix = " (parallel)" if parallel else ""
|
|
128
128
|
# Add inline symbol for step progression
|
|
129
|
-
content = f"
|
|
129
|
+
content = f"❇️ {description}{suffix}"
|
|
130
130
|
return DisplayLine(
|
|
131
131
|
level=2,
|
|
132
132
|
content=content,
|
|
@@ -160,7 +160,7 @@ def format_tool_call(
|
|
|
160
160
|
DisplayLine for tool/subagent call with uniform wrench icon.
|
|
161
161
|
"""
|
|
162
162
|
# Transform to PascalCase for display
|
|
163
|
-
display_name =
|
|
163
|
+
display_name = get_tool_display_name(name)
|
|
164
164
|
|
|
165
165
|
# IG-256: No differentiation - use wrench for all tools/subagents
|
|
166
166
|
icon_emoji = "🔧"
|
|
@@ -318,6 +318,9 @@ def format_reasoning(
|
|
|
318
318
|
IG-XXX: Shows technical reasoning with "Reasoning:" prefix for clarity.
|
|
319
319
|
Uses solid bullet ● (matching goal) to indicate reasoning is active phase.
|
|
320
320
|
|
|
321
|
+
IG-262: Uses level=2 (flat layout) for consistency with judgement lines.
|
|
322
|
+
Reasoning is a sibling to judgement, not a child.
|
|
323
|
+
|
|
321
324
|
Args:
|
|
322
325
|
reasoning: Internal technical analysis text.
|
|
323
326
|
namespace: Event namespace.
|
|
@@ -330,10 +333,10 @@ def format_reasoning(
|
|
|
330
333
|
content = f"💭 {reasoning}"
|
|
331
334
|
|
|
332
335
|
return DisplayLine(
|
|
333
|
-
level=
|
|
336
|
+
level=2, # IG-262: Use level 2 for flat layout (sibling to judgement, not child)
|
|
334
337
|
content=content,
|
|
335
338
|
icon="●", # Solid bullet matching goal icon (polish)
|
|
336
|
-
indent=indent_for_level(
|
|
339
|
+
indent=indent_for_level(2),
|
|
337
340
|
source_prefix=_derive_source_prefix(namespace, verbosity_tier),
|
|
338
341
|
)
|
|
339
342
|
|
|
@@ -350,11 +353,12 @@ def format_judgement(
|
|
|
350
353
|
|
|
351
354
|
IG-089: Shows meaningful judgement info without raw intermediate data.
|
|
352
355
|
IG-XXX: Prominent reasoning display with "Reason:" prefix for clarity.
|
|
356
|
+
IG-265: Removed [new]/[keep] badge from CLI display (kept in event data for logs).
|
|
353
357
|
|
|
354
358
|
Args:
|
|
355
359
|
judgement: Human-readable summary of the decision.
|
|
356
360
|
action: Action taken ("continue" or "complete").
|
|
357
|
-
plan_action:
|
|
361
|
+
plan_action: Ignored (kept for backward compatibility, appears in logs only).
|
|
358
362
|
namespace: Event namespace.
|
|
359
363
|
verbosity_tier: Current verbosity tier.
|
|
360
364
|
|
|
@@ -363,12 +367,8 @@ def format_judgement(
|
|
|
363
367
|
"""
|
|
364
368
|
action_icon = "○" if action == "continue" else "●" # Polish: ○ for continue, ● for complete
|
|
365
369
|
|
|
366
|
-
badge
|
|
367
|
-
|
|
368
|
-
badge = f"[{plan_action}] "
|
|
369
|
-
|
|
370
|
-
# Polish: Add "Reason:" prefix to make LLM reasoning prominent
|
|
371
|
-
content = f"🌀 {badge}{judgement}"
|
|
370
|
+
# IG-265: Remove badge from CLI display (plan_action kept in event data for logs)
|
|
371
|
+
content = f"🌟 {judgement}"
|
|
372
372
|
|
|
373
373
|
return DisplayLine(
|
|
374
374
|
level=2, # Use level 2 for more prominence (like step headers)
|
|
@@ -411,7 +411,7 @@ def format_step_done(
|
|
|
411
411
|
|
|
412
412
|
# Success case: single line
|
|
413
413
|
if success:
|
|
414
|
-
content = f"Done{tool_info}"
|
|
414
|
+
content = f"✓ Done{tool_info}"
|
|
415
415
|
return [
|
|
416
416
|
DisplayLine(
|
|
417
417
|
level=3, # Child node of step header (level 2)
|
|
@@ -427,7 +427,7 @@ def format_step_done(
|
|
|
427
427
|
lines = [
|
|
428
428
|
DisplayLine(
|
|
429
429
|
level=3,
|
|
430
|
-
content=f"Failed{tool_info}",
|
|
430
|
+
content=f"✗ Failed{tool_info}",
|
|
431
431
|
icon="└─", # IG-257: Unicode tree branch (U+2514)
|
|
432
432
|
indent=indent_for_level(3),
|
|
433
433
|
duration_ms=duration_ms,
|
|
@@ -42,6 +42,9 @@ GOAL_COMPLETE_EVENTS = {
|
|
|
42
42
|
"soothe.cognition.agent_loop.completed",
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
# IG-264: Default goal completion message (skip display to avoid redundancy)
|
|
46
|
+
DEFAULT_GOAL_ACHIEVED_MESSAGE = "Goal achieved successfully"
|
|
47
|
+
|
|
45
48
|
# Verbosity tier mapping
|
|
46
49
|
_VERBOSITY_TO_TIER = {
|
|
47
50
|
"quiet": VerbosityTier.QUIET,
|
|
@@ -197,13 +200,16 @@ class StreamDisplayPipeline:
|
|
|
197
200
|
Returns:
|
|
198
201
|
Display lines for goal header.
|
|
199
202
|
"""
|
|
200
|
-
|
|
203
|
+
# IG-262: Prefer friendly_message over goal/goal_description
|
|
204
|
+
friendly_message = event.get("friendly_message")
|
|
205
|
+
goal = friendly_message or event.get("goal", event.get("goal_description", ""))
|
|
201
206
|
if not goal:
|
|
202
207
|
return []
|
|
203
208
|
|
|
204
209
|
# Reset context for new goal
|
|
205
210
|
self._context.reset_goal()
|
|
206
|
-
|
|
211
|
+
# Store the actual goal description (not friendly message) for context tracking
|
|
212
|
+
self._context.current_goal = event.get("goal", event.get("goal_description", goal))
|
|
207
213
|
self._context.goal_start_time = time.time()
|
|
208
214
|
|
|
209
215
|
# Get steps count if available
|
|
@@ -636,7 +642,8 @@ class StreamDisplayPipeline:
|
|
|
636
642
|
)
|
|
637
643
|
else:
|
|
638
644
|
reasoning = event.get("reasoning", "").strip()
|
|
639
|
-
|
|
645
|
+
# IG-264: Skip redundant reasoning when it's the default goal message
|
|
646
|
+
if reasoning and reasoning != DEFAULT_GOAL_ACHIEVED_MESSAGE:
|
|
640
647
|
lines.append(
|
|
641
648
|
format_reasoning(
|
|
642
649
|
reasoning,
|
|
@@ -705,7 +705,7 @@ class EventProcessor:
|
|
|
705
705
|
|
|
706
706
|
# Handle output events (chitchat, quiz, final report, etc.) through unified registry
|
|
707
707
|
# IG-254: Single source of truth for user-visible output events
|
|
708
|
-
if is_output_event(etype):
|
|
708
|
+
if is_output_event(etype) and etype != "soothe.cognition.agent_loop.completed":
|
|
709
709
|
content = extract_output_text(etype, data)
|
|
710
710
|
if content and self._presentation.tier_visible(VerbosityTier.QUIET, self._verbosity):
|
|
711
711
|
cleaned = self._clean_assistant_text(content)
|
|
@@ -444,13 +444,23 @@ def format_tool_call_args(tool_name: str, tool_call: dict[str, Any]) -> str:
|
|
|
444
444
|
values = []
|
|
445
445
|
for key_arg in key_args:
|
|
446
446
|
if key_arg in args:
|
|
447
|
-
|
|
447
|
+
raw_value = args[key_arg]
|
|
448
|
+
value = str(raw_value)
|
|
448
449
|
# Convert and abbreviate path arguments
|
|
449
450
|
if _is_path_arg_name(key_arg):
|
|
450
451
|
value = _display_path_value(value)
|
|
451
452
|
elif len(value) > max_value_length:
|
|
452
453
|
# Truncate non-path long values
|
|
453
454
|
value = value[: max_value_length - 3] + "..."
|
|
455
|
+
# IG-261: Quote string arguments for Task tool (except subagent_type)
|
|
456
|
+
# Only quote if the original value is a string and not already quoted
|
|
457
|
+
if (
|
|
458
|
+
internal == "task"
|
|
459
|
+
and key_arg != "subagent_type"
|
|
460
|
+
and isinstance(raw_value, str)
|
|
461
|
+
and not (value.startswith('"') and value.endswith('"'))
|
|
462
|
+
):
|
|
463
|
+
value = f'"{value}"'
|
|
454
464
|
values.append(value)
|
|
455
465
|
|
|
456
466
|
if not values:
|
|
@@ -13,6 +13,7 @@ from soothe_cli.shared.tool_formatters.file_ops import FileOpsFormatter
|
|
|
13
13
|
from soothe_cli.shared.tool_formatters.goal_formatter import GoalFormatter
|
|
14
14
|
from soothe_cli.shared.tool_formatters.media import MediaFormatter
|
|
15
15
|
from soothe_cli.shared.tool_formatters.structured import StructuredFormatter
|
|
16
|
+
from soothe_cli.shared.tool_formatters.subagent import SubagentFormatter
|
|
16
17
|
from soothe_cli.shared.tool_formatters.web import WebFormatter
|
|
17
18
|
|
|
18
19
|
__all__ = [
|
|
@@ -22,6 +23,7 @@ __all__ = [
|
|
|
22
23
|
"FileOpsFormatter",
|
|
23
24
|
"GoalFormatter",
|
|
24
25
|
"MediaFormatter",
|
|
26
|
+
"SubagentFormatter",
|
|
25
27
|
"StructuredFormatter",
|
|
26
28
|
"WebFormatter",
|
|
27
29
|
]
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"""Subagent tool formatter for brief result display.
|
|
2
|
+
|
|
3
|
+
Task and Research tools should show brief status, not full result content.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from soothe_cli.shared.tool_formatters.base import BaseFormatter
|
|
11
|
+
from soothe_cli.shared.tool_output_formatter import ToolBrief
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class SubagentFormatter(BaseFormatter):
|
|
15
|
+
"""Formatter for subagent tools (task, research).
|
|
16
|
+
|
|
17
|
+
Shows brief completion status without full result content.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def format(self, tool_name: str, result: Any) -> ToolBrief:
|
|
21
|
+
"""Format subagent result with brief status.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
tool_name: Name of the subagent tool (task, research).
|
|
25
|
+
result: Tool result (typically long text output).
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
ToolBrief with brief status and optional short preview.
|
|
29
|
+
|
|
30
|
+
Example:
|
|
31
|
+
>>> formatter = SubagentFormatter()
|
|
32
|
+
>>> brief = formatter.format("task", "Long result text...")
|
|
33
|
+
>>> brief.to_display()
|
|
34
|
+
'✓ Completed'
|
|
35
|
+
"""
|
|
36
|
+
# Handle string results
|
|
37
|
+
if isinstance(result, str):
|
|
38
|
+
# Check for error indicators
|
|
39
|
+
error_indicators = ["error:", "failed:", "exception:", "traceback"]
|
|
40
|
+
is_error = any(indicator in result.lower() for indicator in error_indicators)
|
|
41
|
+
|
|
42
|
+
if is_error:
|
|
43
|
+
# Extract first line of error
|
|
44
|
+
first_line = result.split("\n")[0].strip()
|
|
45
|
+
error_preview = first_line[:80] if len(first_line) > 80 else first_line
|
|
46
|
+
return ToolBrief(
|
|
47
|
+
icon="✗",
|
|
48
|
+
summary="Failed",
|
|
49
|
+
detail=error_preview,
|
|
50
|
+
metrics={"error": True},
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# Success - show brief status only, no content preview
|
|
54
|
+
# Subagent results are typically very long and should not be shown inline
|
|
55
|
+
return ToolBrief(
|
|
56
|
+
icon="✓",
|
|
57
|
+
summary="Completed",
|
|
58
|
+
detail=None, # No content preview
|
|
59
|
+
metrics={"result_length": len(result)},
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# Handle dict results
|
|
63
|
+
if isinstance(result, dict):
|
|
64
|
+
# Check for error field
|
|
65
|
+
if "error" in result:
|
|
66
|
+
error_msg = str(result["error"])[:80]
|
|
67
|
+
return ToolBrief(
|
|
68
|
+
icon="✗",
|
|
69
|
+
summary="Failed",
|
|
70
|
+
detail=error_msg,
|
|
71
|
+
metrics={"error": True},
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# Success - show brief status
|
|
75
|
+
field_count = len(result)
|
|
76
|
+
return ToolBrief(
|
|
77
|
+
icon="✓",
|
|
78
|
+
summary="Completed",
|
|
79
|
+
detail=f"{field_count} fields",
|
|
80
|
+
metrics={"field_count": field_count},
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
# Handle ToolOutput (if available)
|
|
84
|
+
try:
|
|
85
|
+
from soothe_sdk.client.schemas import ToolOutput
|
|
86
|
+
|
|
87
|
+
if isinstance(result, ToolOutput):
|
|
88
|
+
if not result.success:
|
|
89
|
+
error_msg = result.error[:80] if result.error else "Unknown error"
|
|
90
|
+
return ToolBrief(
|
|
91
|
+
icon="✗",
|
|
92
|
+
summary="Failed",
|
|
93
|
+
detail=error_msg,
|
|
94
|
+
metrics={"error": True, "error_type": result.error_type},
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
# Success with ToolOutput
|
|
98
|
+
if result.is_silent_failure():
|
|
99
|
+
return ToolBrief(
|
|
100
|
+
icon="⚠",
|
|
101
|
+
summary="No result",
|
|
102
|
+
detail="Tool succeeded but returned no data",
|
|
103
|
+
metrics={"silent_failure": True},
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
# Has data - show brief status
|
|
107
|
+
return ToolBrief(
|
|
108
|
+
icon="✓",
|
|
109
|
+
summary="Completed",
|
|
110
|
+
detail=None, # No content preview
|
|
111
|
+
metrics={"has_data": result.data is not None},
|
|
112
|
+
)
|
|
113
|
+
except ImportError:
|
|
114
|
+
pass # ToolOutput not available
|
|
115
|
+
|
|
116
|
+
# Fallback for unknown types
|
|
117
|
+
return ToolBrief(
|
|
118
|
+
icon="✓",
|
|
119
|
+
summary="Completed",
|
|
120
|
+
detail=None,
|
|
121
|
+
metrics={},
|
|
122
|
+
)
|
|
@@ -161,6 +161,7 @@ class ToolOutputFormatter:
|
|
|
161
161
|
GoalFormatter,
|
|
162
162
|
MediaFormatter,
|
|
163
163
|
StructuredFormatter,
|
|
164
|
+
SubagentFormatter,
|
|
164
165
|
WebFormatter,
|
|
165
166
|
)
|
|
166
167
|
|
|
@@ -176,6 +177,9 @@ class ToolOutputFormatter:
|
|
|
176
177
|
if category == "execution":
|
|
177
178
|
formatter = ExecutionFormatter()
|
|
178
179
|
return formatter.format(tool_name, result)
|
|
180
|
+
if category == "subagent":
|
|
181
|
+
formatter = SubagentFormatter()
|
|
182
|
+
return formatter.format(tool_name, result)
|
|
179
183
|
if category == "media":
|
|
180
184
|
formatter = MediaFormatter()
|
|
181
185
|
return formatter.format(tool_name, result)
|
|
@@ -10,7 +10,7 @@ from contextlib import suppress
|
|
|
10
10
|
from pathlib import Path
|
|
11
11
|
from typing import Any
|
|
12
12
|
|
|
13
|
-
from soothe_sdk.utils import get_all_path_arg_keys,
|
|
13
|
+
from soothe_sdk.utils import get_all_path_arg_keys, get_tool_display_name, get_tool_meta
|
|
14
14
|
|
|
15
15
|
from soothe_cli.shared.message_processing import (
|
|
16
16
|
_normalize_tool_name_for_arg_map,
|
|
@@ -136,7 +136,7 @@ def format_tool_display(tool_name: str, tool_args: dict) -> str:
|
|
|
136
136
|
tool_args = extract_tool_args_dict(tool_args) if tool_args else {}
|
|
137
137
|
tool_key = _normalize_tool_name_for_arg_map(tool_name or "")
|
|
138
138
|
# Get PascalCase display name for all return statements
|
|
139
|
-
pascal_name =
|
|
139
|
+
pascal_name = get_tool_display_name(tool_key)
|
|
140
140
|
|
|
141
141
|
def abbreviate_path(path_str: str, max_length: int = 60) -> str:
|
|
142
142
|
"""Abbreviate a file path intelligently - show basename or relative path.
|
|
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.4.0 → soothe_cli-0.4.1}/src/soothe_cli/shared/tool_formatters/goal_formatter.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
|