deepy-cli 0.2.27__tar.gz → 0.2.28__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/PKG-INFO +1 -1
  2. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/pyproject.toml +1 -1
  3. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/__init__.py +1 -1
  4. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/tui/app.py +75 -0
  5. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/tui/widgets.py +69 -5
  6. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/README.md +0 -0
  7. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/__main__.py +0 -0
  8. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/audit.py +0 -0
  9. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/background_tasks.py +0 -0
  10. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/cli.py +0 -0
  11. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/config/__init__.py +0 -0
  12. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/config/settings.py +0 -0
  13. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/data/__init__.py +0 -0
  14. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/data/skills/skill-creator/SKILL.md +0 -0
  15. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/data/skills/skill-installer/SKILL.md +0 -0
  16. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/data/tools/AskUserQuestion.md +0 -0
  17. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/data/tools/Read.md +0 -0
  18. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/data/tools/Search.md +0 -0
  19. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/data/tools/Update.md +0 -0
  20. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/data/tools/WebFetch.md +0 -0
  21. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/data/tools/WebSearch.md +0 -0
  22. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/data/tools/Write.md +0 -0
  23. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/data/tools/__init__.py +0 -0
  24. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/data/tools/shell.md +0 -0
  25. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/data/tools/task_list.md +0 -0
  26. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/data/tools/task_output.md +0 -0
  27. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/data/tools/task_stop.md +0 -0
  28. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/data/tools/test_shell.md +0 -0
  29. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/data/tools/todo_write.md +0 -0
  30. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/errors.py +0 -0
  31. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/input_suggestions.py +0 -0
  32. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/llm/__init__.py +0 -0
  33. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/llm/agent.py +0 -0
  34. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/llm/cache_context.py +0 -0
  35. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/llm/compaction.py +0 -0
  36. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/llm/context.py +0 -0
  37. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/llm/events.py +0 -0
  38. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/llm/model_capabilities.py +0 -0
  39. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/llm/multimodal.py +0 -0
  40. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/llm/provider.py +0 -0
  41. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/llm/replay.py +0 -0
  42. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/llm/runner.py +0 -0
  43. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/llm/thinking.py +0 -0
  44. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/mcp.py +0 -0
  45. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/prompts/__init__.py +0 -0
  46. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/prompts/compact.py +0 -0
  47. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/prompts/init_agents.py +0 -0
  48. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/prompts/rules.py +0 -0
  49. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/prompts/runtime_context.py +0 -0
  50. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/prompts/system.py +0 -0
  51. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/prompts/tool_docs.py +0 -0
  52. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/session_cost.py +0 -0
  53. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/sessions/__init__.py +0 -0
  54. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/sessions/index.py +0 -0
  55. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/sessions/manager.py +0 -0
  56. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/sessions/session.py +0 -0
  57. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/sessions/store_helpers.py +0 -0
  58. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/skill_market.py +0 -0
  59. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/skills.py +0 -0
  60. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/status.py +0 -0
  61. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/subagents.py +0 -0
  62. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/todos.py +0 -0
  63. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/tools/__init__.py +0 -0
  64. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/tools/agents.py +0 -0
  65. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/tools/builtin.py +0 -0
  66. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/tools/file_state.py +0 -0
  67. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/tools/result.py +0 -0
  68. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/tools/search.py +0 -0
  69. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/tools/shell_output.py +0 -0
  70. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/tools/shell_utils.py +0 -0
  71. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/tools/test_shell.py +0 -0
  72. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/tui/__init__.py +0 -0
  73. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/tui/commands.py +0 -0
  74. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/tui/compat.py +0 -0
  75. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/tui/diff.py +0 -0
  76. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/tui/interaction_surfaces.py +0 -0
  77. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/tui/runner.py +0 -0
  78. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/tui/screens.py +0 -0
  79. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/tui/state.py +0 -0
  80. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/tui/theme.py +0 -0
  81. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/tui/transcript.py +0 -0
  82. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/types/__init__.py +0 -0
  83. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/types/sdk.py +0 -0
  84. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/types/tool_payloads.py +0 -0
  85. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/__init__.py +0 -0
  86. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/app.py +0 -0
  87. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/ask_user_question.py +0 -0
  88. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/audit_approval_panel.py +0 -0
  89. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/audit_approval_picker.py +0 -0
  90. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/exit_summary.py +0 -0
  91. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/file_mentions.py +0 -0
  92. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/image_input.py +0 -0
  93. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/loading_text.py +0 -0
  94. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/local_command.py +0 -0
  95. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/markdown.py +0 -0
  96. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/message_view.py +0 -0
  97. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/model_picker.py +0 -0
  98. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/prompt_buffer.py +0 -0
  99. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/prompt_input.py +0 -0
  100. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/session_list.py +0 -0
  101. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/session_picker.py +0 -0
  102. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/skill_picker.py +0 -0
  103. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/slash_commands.py +0 -0
  104. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/status_footer.py +0 -0
  105. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/styles.py +0 -0
  106. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/syntax.py +0 -0
  107. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/terminal.py +0 -0
  108. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/theme_picker.py +0 -0
  109. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/thinking_state.py +0 -0
  110. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/welcome.py +0 -0
  111. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/update_check.py +0 -0
  112. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/usage.py +0 -0
  113. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/utils/__init__.py +0 -0
  114. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/utils/debug_logger.py +0 -0
  115. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/utils/error_logger.py +0 -0
  116. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/utils/json.py +0 -0
  117. {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/utils/notify.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: deepy-cli
3
- Version: 0.2.27
3
+ Version: 0.2.28
4
4
  Summary: Deepy - Vibe coding for DeepSeek models in your terminal
5
5
  Keywords: deepseek,coding-agent,terminal,cli,agents
6
6
  Author: kirineko
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "deepy-cli"
3
- version = "0.2.27"
3
+ version = "0.2.28"
4
4
  description = "Deepy - Vibe coding for DeepSeek models in your terminal"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.2.27"
3
+ __version__ = "0.2.28"
4
4
 
5
5
 
6
6
  def main() -> None:
@@ -224,6 +224,7 @@ class DeepyTuiApp(App[None]):
224
224
  BINDINGS = [
225
225
  Binding("ctrl+d", "confirm_quit", "Quit", priority=True),
226
226
  Binding("escape", "interrupt_or_focus_prompt", "Interrupt"),
227
+ Binding("ctrl+c,super+c", "copy_focused_block", "Copy", show=False),
227
228
  Binding("ctrl+o", "toggle_help_panel", "Panel"),
228
229
  Binding("shift+tab", "cycle_audit_mode", "Audit", priority=True),
229
230
  Binding("alt+up", "focus_previous_block", "Previous block"),
@@ -472,12 +473,38 @@ class DeepyTuiApp(App[None]):
472
473
  color: $text;
473
474
  }
474
475
 
476
+ .subagent-parameters {
477
+ margin: 0 0 0 2;
478
+ padding: 0 0 0 1;
479
+ border-left: solid $secondary;
480
+ color: $text-muted;
481
+ }
482
+
475
483
  .tool-details {
476
484
  margin: 1 0 0 0;
477
485
  color: $text-muted;
478
486
  display: none;
479
487
  }
480
488
 
489
+ .subagent-block .tool-details {
490
+ margin: 0 0 0 2;
491
+ padding: 0 0 0 1;
492
+ border-left: solid $accent;
493
+ color: $text;
494
+ }
495
+
496
+ .subagent-block.-running .tool-details {
497
+ border-left: solid $accent;
498
+ }
499
+
500
+ .subagent-block.-ok .tool-details {
501
+ border-left: solid $success;
502
+ }
503
+
504
+ .subagent-block.-failed .tool-details {
505
+ border-left: solid $error;
506
+ }
507
+
481
508
  .tool-block.-retryable .block-title {
482
509
  color: $warning;
483
510
  }
@@ -2749,6 +2776,27 @@ class DeepyTuiApp(App[None]):
2749
2776
  prompt.prepare_clear_on_next_delete()
2750
2777
  prompt.focus()
2751
2778
 
2779
+ def action_copy_focused_block(self) -> None:
2780
+ block = self._focused_transcript_block()
2781
+ if block is None:
2782
+ self._update_status("Focus a transcript block to copy")
2783
+ return
2784
+ text = _transcript_block_copy_text(block).strip()
2785
+ if not text:
2786
+ self._update_status("Nothing to copy")
2787
+ return
2788
+ self.copy_to_clipboard(text)
2789
+ self._update_status("Copied transcript block")
2790
+
2791
+ def _focused_transcript_block(self) -> Widget | None:
2792
+ node = self.focused
2793
+ while isinstance(node, Widget):
2794
+ if node.has_class("transcript-block"):
2795
+ return node
2796
+ parent = node.parent
2797
+ node = parent if isinstance(parent, Widget) else None
2798
+ return None
2799
+
2752
2800
  def action_focus_next_block(self) -> None:
2753
2801
  blocks = list(self.query(".transcript-block"))
2754
2802
  if not blocks:
@@ -2771,6 +2819,33 @@ class DeepyTuiApp(App[None]):
2771
2819
  panel.toggle_class("-visible")
2772
2820
 
2773
2821
 
2822
+ def _transcript_block_copy_text(block: Widget) -> str:
2823
+ if isinstance(block, UserBlock):
2824
+ return block.body
2825
+ if isinstance(block, AssistantBlock):
2826
+ return block.markdown
2827
+ if isinstance(block, ThinkingBlock):
2828
+ return block.body
2829
+ if isinstance(block, LocalCommandBlock):
2830
+ parts = [block.title, block.output_body]
2831
+ if block.meta_body:
2832
+ parts.append(block.meta_body)
2833
+ return "\n".join(part for part in parts if part)
2834
+ if isinstance(block, ToolBlock):
2835
+ parts = [block.title]
2836
+ if block.tool_name == "todo_write" and block.output_body:
2837
+ parts.append(block.output_body)
2838
+ if block.expanded and block.details:
2839
+ parts.append(block.details)
2840
+ return "\n\n".join(part for part in parts if part)
2841
+ if isinstance(block, DiffBlock):
2842
+ return block.body
2843
+ if isinstance(block, InfoBlock):
2844
+ return block.body
2845
+ body = getattr(block, "body", "")
2846
+ return body if isinstance(body, str) else ""
2847
+
2848
+
2774
2849
  async def _load_session_items(project_root: Path, session_id: str) -> list[dict[str, Any]]:
2775
2850
  try:
2776
2851
  return await DeepySession.open(project_root, session_id).get_items()
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import contextlib
3
4
  import time
4
5
  from dataclasses import dataclass
5
6
  from pathlib import Path
@@ -743,6 +744,8 @@ class ToolBlock(TranscriptBlock):
743
744
  recovered_from_retry: bool = False,
744
745
  ) -> None:
745
746
  classes = "tool-block todo-block" if tool_name == "todo_write" else transcript_display("tool").css_class
747
+ if _is_subagent_tool_name(tool_name):
748
+ classes += " subagent-block"
746
749
  if retryable:
747
750
  classes += " -retryable"
748
751
  super().__init__(label, body, classes=classes, kind="tool")
@@ -765,6 +768,7 @@ class ToolBlock(TranscriptBlock):
765
768
  title = f"{display_name} running"
766
769
  if name == "shell" and params:
767
770
  title = f"{title} - {params}"
771
+ details = _subagent_details(params, "") if _is_subagent_tool_name(name) else ""
768
772
  body = (
769
773
  "Waiting for user input."
770
774
  if name == "AskUserQuestion"
@@ -777,6 +781,7 @@ class ToolBlock(TranscriptBlock):
777
781
  body,
778
782
  call_id=call_id,
779
783
  arguments=params,
784
+ details=details,
780
785
  tool_name=name or "tool",
781
786
  )
782
787
 
@@ -809,7 +814,7 @@ class ToolBlock(TranscriptBlock):
809
814
  self.title = f"{self.title} - {params}"
810
815
  self.output_body = "Running"
811
816
  self.body = ""
812
- self.details = ""
817
+ self.details = _subagent_details(params, "") if _is_subagent_tool_name(self.tool_name) else ""
813
818
  self.waiting_for_user = False
814
819
  self.retryable = False
815
820
  self.recovered_from_retry = True
@@ -818,7 +823,7 @@ class ToolBlock(TranscriptBlock):
818
823
  self._update_visible_output()
819
824
  details = self.query_one(".tool-details", Static)
820
825
  details.update(self.details)
821
- details.display = False
826
+ details.display = self._details_visible()
822
827
  self.set_class(False, "-waiting")
823
828
  self.set_class(False, "-retryable")
824
829
  self._sync_status_classes()
@@ -832,7 +837,11 @@ class ToolBlock(TranscriptBlock):
832
837
  )
833
838
  output_body = _tool_output_body(view)
834
839
  self.output_body = output_body
835
- self.details = _tool_output_details(view)
840
+ self.details = (
841
+ _subagent_details(self.arguments, output_body)
842
+ if _is_subagent_tool_name(view.name)
843
+ else _tool_output_details(view)
844
+ )
836
845
  self.waiting_for_user = view.await_user_response
837
846
  self.retryable = view.status == "retryable"
838
847
  self.status_state = "waiting" if self.waiting_for_user else view.status
@@ -841,16 +850,20 @@ class ToolBlock(TranscriptBlock):
841
850
  self._update_visible_output()
842
851
  details = self.query_one(".tool-details", Static)
843
852
  details.update(self.details)
844
- details.display = bool(self.details and self.expanded)
853
+ details.display = self._details_visible()
845
854
  self.set_class(self.waiting_for_user, "-waiting")
846
855
  self.set_class(self.retryable, "-retryable")
847
856
  self._sync_status_classes()
848
857
  self.set_class(view.name == "todo_write", "todo-block")
858
+ self.set_class(_is_subagent_tool_name(view.name), "subagent-block")
849
859
 
850
860
  def compose(self) -> ComposeResult:
851
861
  with Horizontal(classes="role-line tool-role-line"):
852
862
  yield Label(self.display_model.label, classes="block-title role-marker tool-marker")
853
863
  yield Static(self.title, classes="block-body tool-summary")
864
+ params = Static(_subagent_parameters(self.arguments), classes="block-body subagent-parameters")
865
+ params.display = self._subagent_parameters_visible()
866
+ yield params
854
867
  visible_output = _tool_output_visible(self.tool_name, self.output_body)
855
868
  output = Static(
856
869
  _tool_output_renderable(self.tool_name, self.output_body) if visible_output else "",
@@ -864,7 +877,7 @@ class ToolBlock(TranscriptBlock):
864
877
 
865
878
  def action_toggle_expand(self) -> None:
866
879
  super().action_toggle_expand()
867
- self.query_one(".tool-details", Static).display = False
880
+ self.query_one(".tool-details", Static).display = self._details_visible()
868
881
 
869
882
  def _sync_status_classes(self) -> None:
870
883
  self.set_class(self.status_state == "running", "-running")
@@ -874,11 +887,21 @@ class ToolBlock(TranscriptBlock):
874
887
  self.set_class(self.status_state == "failed", "-failed")
875
888
 
876
889
  def _update_visible_output(self) -> None:
890
+ with contextlib.suppress(NoMatches):
891
+ params = self.query_one(".subagent-parameters", Static)
892
+ params.update(_subagent_parameters(self.arguments))
893
+ params.display = self._subagent_parameters_visible()
877
894
  output = self.query_one(".tool-output", Static)
878
895
  visible_output = _tool_output_visible(self.tool_name, self.output_body)
879
896
  output.update(_tool_output_renderable(self.tool_name, self.output_body) if visible_output else "")
880
897
  output.display = visible_output
881
898
 
899
+ def _details_visible(self) -> bool:
900
+ return bool(self.details and self.expanded and _is_subagent_tool_name(self.tool_name))
901
+
902
+ def _subagent_parameters_visible(self) -> bool:
903
+ return bool(self.arguments and _is_subagent_tool_name(self.tool_name))
904
+
882
905
 
883
906
  class LocalCommandBlock(Vertical, can_focus=True):
884
907
  def __init__(self, view: ToolOutputView, *, call_id: str = "") -> None:
@@ -1430,6 +1453,10 @@ def _tool_arguments_body(name: str, arguments: str) -> str:
1430
1453
  return ""
1431
1454
  if not arguments.strip():
1432
1455
  return ""
1456
+ if _is_subagent_tool_name(name):
1457
+ task = _subagent_input_argument(arguments)
1458
+ if task:
1459
+ return task
1433
1460
  if name == "shell":
1434
1461
  command = _shell_command_argument(arguments)
1435
1462
  if command:
@@ -1448,6 +1475,41 @@ def _shell_command_argument(arguments: str) -> str:
1448
1475
  return command.strip() if isinstance(command, str) else ""
1449
1476
 
1450
1477
 
1478
+ def _is_subagent_tool_name(name: str) -> bool:
1479
+ return name.startswith("subagent_")
1480
+
1481
+
1482
+ def _subagent_input_argument(arguments: str) -> str:
1483
+ try:
1484
+ args = json_utils.loads(arguments)
1485
+ except json_utils.JSONDecodeError:
1486
+ return ""
1487
+ if not isinstance(args, dict):
1488
+ return ""
1489
+ value = args.get("input") or args.get("task") or args.get("prompt")
1490
+ return value.strip() if isinstance(value, str) else ""
1491
+
1492
+
1493
+ def _subagent_details(task: str, report: str) -> str:
1494
+ parts: list[str] = []
1495
+ compact_task = _compact_text(task, max_lines=4, max_chars=500)
1496
+ if compact_task:
1497
+ parts.extend(["Task", _indent_block(compact_task)])
1498
+ compact_report = _compact_text(report, max_lines=16, max_chars=1600)
1499
+ if compact_report:
1500
+ if parts:
1501
+ parts.append("")
1502
+ parts.extend(["Report", _indent_block(compact_report)])
1503
+ return "\n".join(parts)
1504
+
1505
+
1506
+ def _subagent_parameters(task: str) -> str:
1507
+ compact = _compact_text(task, max_lines=4, max_chars=700)
1508
+ if not compact:
1509
+ return ""
1510
+ return "Subagent Parameters\n" + _indent_block(compact)
1511
+
1512
+
1451
1513
  def _tool_output_body(view: ToolOutputView) -> str:
1452
1514
  if view.name == "AskUserQuestion":
1453
1515
  return "Waiting for user input." if view.await_user_response else _compact_text(view.output or view.summary)
@@ -1470,6 +1532,8 @@ def _tool_output_body(view: ToolOutputView) -> str:
1470
1532
  return _todo_body(view)
1471
1533
  if view.name in {"WebSearch", "WebFetch"}:
1472
1534
  return _web_body(view)
1535
+ if _is_subagent_tool_name(view.name):
1536
+ return (view.error or view.output or view.summary).strip()
1473
1537
  if _is_mcp_view(view):
1474
1538
  return _mcp_body(view)
1475
1539
  if view.ok is False and view.metadata:
File without changes
File without changes
File without changes