tsugite-cli 0.3.3__py3-none-any.whl

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 (101) hide show
  1. tsugite/__init__.py +6 -0
  2. tsugite/agent_composition.py +163 -0
  3. tsugite/agent_inheritance.py +479 -0
  4. tsugite/agent_preparation.py +236 -0
  5. tsugite/agent_runner/__init__.py +45 -0
  6. tsugite/agent_runner/helpers.py +106 -0
  7. tsugite/agent_runner/history_integration.py +248 -0
  8. tsugite/agent_runner/metrics.py +100 -0
  9. tsugite/agent_runner/runner.py +1879 -0
  10. tsugite/agent_runner/validation.py +70 -0
  11. tsugite/agent_utils.py +167 -0
  12. tsugite/attachments/__init__.py +65 -0
  13. tsugite/attachments/auto_context.py +199 -0
  14. tsugite/attachments/base.py +34 -0
  15. tsugite/attachments/file.py +51 -0
  16. tsugite/attachments/inline.py +31 -0
  17. tsugite/attachments/storage.py +178 -0
  18. tsugite/attachments/url.py +59 -0
  19. tsugite/attachments/youtube.py +101 -0
  20. tsugite/benchmark/__init__.py +62 -0
  21. tsugite/benchmark/config.py +183 -0
  22. tsugite/benchmark/core.py +292 -0
  23. tsugite/benchmark/discovery.py +377 -0
  24. tsugite/benchmark/evaluators.py +671 -0
  25. tsugite/benchmark/execution.py +657 -0
  26. tsugite/benchmark/metrics.py +204 -0
  27. tsugite/benchmark/reports.py +420 -0
  28. tsugite/benchmark/utils.py +288 -0
  29. tsugite/builtin_agents/chat-assistant.md +53 -0
  30. tsugite/builtin_agents/default.md +140 -0
  31. tsugite/builtin_agents.py +5 -0
  32. tsugite/cache.py +195 -0
  33. tsugite/cli/__init__.py +1042 -0
  34. tsugite/cli/agents.py +148 -0
  35. tsugite/cli/attachments.py +193 -0
  36. tsugite/cli/benchmark.py +663 -0
  37. tsugite/cli/cache.py +113 -0
  38. tsugite/cli/config.py +272 -0
  39. tsugite/cli/helpers.py +534 -0
  40. tsugite/cli/history.py +193 -0
  41. tsugite/cli/init.py +387 -0
  42. tsugite/cli/mcp.py +193 -0
  43. tsugite/cli/tools.py +419 -0
  44. tsugite/config.py +204 -0
  45. tsugite/console.py +48 -0
  46. tsugite/constants.py +21 -0
  47. tsugite/core/__init__.py +19 -0
  48. tsugite/core/agent.py +774 -0
  49. tsugite/core/executor.py +300 -0
  50. tsugite/core/memory.py +67 -0
  51. tsugite/core/tools.py +271 -0
  52. tsugite/docker_cli.py +270 -0
  53. tsugite/events/__init__.py +55 -0
  54. tsugite/events/base.py +46 -0
  55. tsugite/events/bus.py +62 -0
  56. tsugite/events/events.py +224 -0
  57. tsugite/exceptions.py +40 -0
  58. tsugite/history/__init__.py +29 -0
  59. tsugite/history/index.py +210 -0
  60. tsugite/history/models.py +106 -0
  61. tsugite/history/storage.py +157 -0
  62. tsugite/mcp_client.py +219 -0
  63. tsugite/mcp_config.py +174 -0
  64. tsugite/md_agents.py +751 -0
  65. tsugite/models.py +257 -0
  66. tsugite/renderer.py +151 -0
  67. tsugite/shell_tool_config.py +265 -0
  68. tsugite/templates/assistant.md +14 -0
  69. tsugite/tools/__init__.py +265 -0
  70. tsugite/tools/agents.py +312 -0
  71. tsugite/tools/edit_strategies.py +393 -0
  72. tsugite/tools/fs.py +329 -0
  73. tsugite/tools/http.py +239 -0
  74. tsugite/tools/interactive.py +430 -0
  75. tsugite/tools/shell.py +129 -0
  76. tsugite/tools/shell_tools.py +214 -0
  77. tsugite/tools/tasks.py +339 -0
  78. tsugite/tsugite.py +7 -0
  79. tsugite/ui/__init__.py +46 -0
  80. tsugite/ui/base.py +638 -0
  81. tsugite/ui/chat.py +265 -0
  82. tsugite/ui/chat.tcss +92 -0
  83. tsugite/ui/chat_history.py +286 -0
  84. tsugite/ui/helpers.py +102 -0
  85. tsugite/ui/jsonl.py +125 -0
  86. tsugite/ui/live_template.py +529 -0
  87. tsugite/ui/plain.py +419 -0
  88. tsugite/ui/textual_chat.py +642 -0
  89. tsugite/ui/textual_handler.py +225 -0
  90. tsugite/ui/widgets/__init__.py +6 -0
  91. tsugite/ui/widgets/base_scroll_log.py +27 -0
  92. tsugite/ui/widgets/message_list.py +121 -0
  93. tsugite/ui/widgets/thought_log.py +80 -0
  94. tsugite/ui_context.py +90 -0
  95. tsugite/utils.py +367 -0
  96. tsugite/xdg.py +104 -0
  97. tsugite_cli-0.3.3.dist-info/METADATA +325 -0
  98. tsugite_cli-0.3.3.dist-info/RECORD +101 -0
  99. tsugite_cli-0.3.3.dist-info/WHEEL +4 -0
  100. tsugite_cli-0.3.3.dist-info/entry_points.txt +5 -0
  101. tsugite_cli-0.3.3.dist-info/licenses/LICENSE +235 -0
tsugite/ui/helpers.py ADDED
@@ -0,0 +1,102 @@
1
+ """Helper functions for creating and managing UI loggers."""
2
+
3
+ from contextlib import contextmanager
4
+ from typing import Generator
5
+
6
+ from rich.console import Console
7
+
8
+ from tsugite.ui.base import CustomUIHandler, CustomUILogger
9
+ from tsugite.ui.plain import PlainUIHandler
10
+ from tsugite.ui_context import clear_ui_context, set_ui_context
11
+
12
+
13
+ @contextmanager
14
+ def custom_agent_ui(
15
+ console: Console,
16
+ show_code: bool = True,
17
+ show_observations: bool = True,
18
+ show_progress: bool = True,
19
+ show_llm_messages: bool = False,
20
+ show_execution_results: bool = True,
21
+ show_execution_logs: bool = True,
22
+ show_panels: bool = True,
23
+ show_debug_messages: bool = False,
24
+ ) -> Generator[CustomUILogger, None, None]:
25
+ """Context manager for custom agent UI.
26
+
27
+ Args:
28
+ console: Rich console instance
29
+ show_code: Whether to display executed code
30
+ show_observations: Whether to display tool observations
31
+ show_progress: Whether to show progress spinner
32
+ show_llm_messages: Whether to show LLM reasoning messages
33
+ show_execution_results: Whether to show code execution results
34
+ show_execution_logs: Whether to show execution logs
35
+ show_panels: Whether to show Rich panels (borders and decorations)
36
+ show_debug_messages: Whether to show debug messages (requires --verbose)
37
+
38
+ Yields:
39
+ CustomUILogger: Logger instance with ui_handler and console
40
+ """
41
+ ui_handler = CustomUIHandler(
42
+ console,
43
+ show_code=show_code,
44
+ show_observations=show_observations,
45
+ show_llm_messages=show_llm_messages,
46
+ show_execution_results=show_execution_results,
47
+ show_execution_logs=show_execution_logs,
48
+ show_panels=show_panels,
49
+ show_debug_messages=show_debug_messages,
50
+ )
51
+ logger = CustomUILogger(ui_handler, console)
52
+
53
+ try:
54
+ if show_progress:
55
+ # progress_context() will call set_ui_context() with the progress instance
56
+ with ui_handler.progress_context():
57
+ yield logger
58
+ else:
59
+ # No progress, so set ui_context here without progress
60
+ set_ui_context(console=console, progress=None, ui_handler=ui_handler)
61
+ yield logger
62
+ finally:
63
+ clear_ui_context()
64
+
65
+
66
+ def create_plain_logger() -> CustomUILogger:
67
+ """Create a plain text logger for copy-paste friendly output.
68
+
69
+ Returns a logger using PlainUIHandler with no colors, panels, or emojis.
70
+ Ideal for:
71
+ - Piped output
72
+ - Copy-paste workflows
73
+ - Screen readers
74
+ - Logs and automation
75
+
76
+ Returns:
77
+ CustomUILogger with PlainUIHandler
78
+ """
79
+ plain_handler = PlainUIHandler()
80
+ return CustomUILogger(plain_handler, plain_handler.console)
81
+
82
+
83
+ def create_live_template_logger(interactive: bool = True) -> CustomUILogger:
84
+ """Create logger using Live Template handler with Tree and interactive prompts.
85
+
86
+ Returns a logger using LiveTemplateHandler with Rich Live Display.
87
+ Features:
88
+ - Live Display with multi-panel layout
89
+ - Tree visualization of execution steps
90
+ - Interactive prompts (optional)
91
+ - Real-time updates without scrolling
92
+
93
+ Args:
94
+ interactive: Enable interactive prompts during execution
95
+
96
+ Returns:
97
+ CustomUILogger with LiveTemplateHandler
98
+ """
99
+ from tsugite.ui.live_template import LiveTemplateHandler
100
+
101
+ handler = LiveTemplateHandler(interactive=interactive)
102
+ return CustomUILogger(handler, handler.console)
tsugite/ui/jsonl.py ADDED
@@ -0,0 +1,125 @@
1
+ """JSONL UI handler for subprocess-based subagent communication."""
2
+
3
+ import json
4
+ from typing import Any, Dict
5
+
6
+ from tsugite.events import (
7
+ BaseEvent,
8
+ CodeExecutionEvent,
9
+ ErrorEvent,
10
+ ExecutionResultEvent,
11
+ FinalAnswerEvent,
12
+ LLMMessageEvent,
13
+ ObservationEvent,
14
+ StepStartEvent,
15
+ TaskStartEvent,
16
+ ToolCallEvent,
17
+ )
18
+
19
+
20
+ class JSONLUIHandler:
21
+ """Emit UI events as JSONL to stdout for subprocess communication.
22
+
23
+ This handler converts all UI events to line-delimited JSON objects,
24
+ enabling parent agents to monitor subagent progress in real-time.
25
+
26
+ JSONL Protocol Schema:
27
+ ----------------------
28
+ Each line is a JSON object with a "type" field and type-specific data.
29
+
30
+ Event Type Mappings:
31
+ - TaskStartEvent → {"type": "init", "agent": str, "model": str}
32
+ - StepStartEvent → {"type": "turn_start", "turn": int}
33
+ - LLMMessageEvent → {"type": "thought", "content": str}
34
+ - CodeExecutionEvent → {"type": "code", "content": str}
35
+ - ToolCallEvent → {"type": "tool_call", "tool": str, "args": dict}
36
+ - ObservationEvent → {"type": "tool_result", "tool": str, "success": bool, "output"?: str, "error"?: str}
37
+ - ExecutionResultEvent→ {"type": "tool_result", "tool": "code_execution", "success": bool, "output"?: str, "error"?: str}
38
+ - FinalAnswerEvent → {"type": "final_result", "result": str, "turns": int, "tokens": int, "cost": float}
39
+ - ErrorEvent → {"type": "error", "error": str, "step": int}
40
+
41
+ Success/Failure Patterns:
42
+ - Successful tool: {"type": "tool_result", "tool": "read_file", "success": true, "output": "..."}
43
+ - Failed tool: {"type": "tool_result", "tool": "read_file", "success": false, "error": "..."}
44
+ - Code execution success: {"type": "tool_result", "tool": "code_execution", "success": true, "output": "logs\\nresult"}
45
+ - Code execution failure: {"type": "tool_result", "tool": "code_execution", "success": false, "error": "..."}
46
+ """
47
+
48
+ def handle_event(self, event: BaseEvent) -> None:
49
+ """Convert UI event to JSONL and print to stdout.
50
+
51
+ Args:
52
+ event: The Pydantic event
53
+ """
54
+ if isinstance(event, TaskStartEvent):
55
+ self._emit("init", {"agent": event.task, "model": event.model})
56
+
57
+ elif isinstance(event, StepStartEvent):
58
+ self._emit("turn_start", {"turn": event.step})
59
+
60
+ elif isinstance(event, LLMMessageEvent):
61
+ if event.content.strip():
62
+ self._emit("thought", {"content": event.content})
63
+
64
+ elif isinstance(event, CodeExecutionEvent):
65
+ if event.code:
66
+ self._emit("code", {"content": event.code})
67
+
68
+ elif isinstance(event, ToolCallEvent):
69
+ self._emit("tool_call", {"tool": event.tool, "args": event.args})
70
+
71
+ elif isinstance(event, ObservationEvent):
72
+ if event.success:
73
+ self._emit(
74
+ "tool_result", {"tool": event.tool or "unknown", "success": True, "output": event.observation}
75
+ )
76
+ else:
77
+ self._emit(
78
+ "tool_result",
79
+ {"tool": event.tool or "unknown", "success": False, "error": event.error or event.observation},
80
+ )
81
+
82
+ elif isinstance(event, ExecutionResultEvent):
83
+ if event.success:
84
+ # Combine logs and output for backward compatibility
85
+ output = event.output or ""
86
+ if event.logs:
87
+ logs_str = "\n".join(event.logs)
88
+ output = f"{logs_str}\n{output}" if output else logs_str
89
+ self._emit("tool_result", {"tool": "code_execution", "success": True, "output": output})
90
+ else:
91
+ self._emit("tool_result", {"tool": "code_execution", "success": False, "error": event.error or ""})
92
+
93
+ elif isinstance(event, FinalAnswerEvent):
94
+ self._emit(
95
+ "final_result",
96
+ {
97
+ "result": event.answer,
98
+ "turns": event.turns,
99
+ "tokens": event.tokens,
100
+ "cost": event.cost,
101
+ },
102
+ )
103
+
104
+ elif isinstance(event, ErrorEvent):
105
+ self._emit("error", {"error": event.error, "step": event.step})
106
+
107
+ def _emit(self, event_type: str, data: Dict[str, Any]) -> None:
108
+ """Print JSONL event to stdout.
109
+
110
+ Args:
111
+ event_type: The event type string
112
+ data: Event-specific data dictionary
113
+ """
114
+ event = {"type": event_type, **data}
115
+ print(json.dumps(event), flush=True)
116
+
117
+ def update_progress(self, description: str) -> None:
118
+ """No-op for progress updates in JSONL mode."""
119
+ pass
120
+
121
+ def progress_context(self):
122
+ """No-op context manager for compatibility."""
123
+ from contextlib import nullcontext
124
+
125
+ return nullcontext()