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.
- tsugite/__init__.py +6 -0
- tsugite/agent_composition.py +163 -0
- tsugite/agent_inheritance.py +479 -0
- tsugite/agent_preparation.py +236 -0
- tsugite/agent_runner/__init__.py +45 -0
- tsugite/agent_runner/helpers.py +106 -0
- tsugite/agent_runner/history_integration.py +248 -0
- tsugite/agent_runner/metrics.py +100 -0
- tsugite/agent_runner/runner.py +1879 -0
- tsugite/agent_runner/validation.py +70 -0
- tsugite/agent_utils.py +167 -0
- tsugite/attachments/__init__.py +65 -0
- tsugite/attachments/auto_context.py +199 -0
- tsugite/attachments/base.py +34 -0
- tsugite/attachments/file.py +51 -0
- tsugite/attachments/inline.py +31 -0
- tsugite/attachments/storage.py +178 -0
- tsugite/attachments/url.py +59 -0
- tsugite/attachments/youtube.py +101 -0
- tsugite/benchmark/__init__.py +62 -0
- tsugite/benchmark/config.py +183 -0
- tsugite/benchmark/core.py +292 -0
- tsugite/benchmark/discovery.py +377 -0
- tsugite/benchmark/evaluators.py +671 -0
- tsugite/benchmark/execution.py +657 -0
- tsugite/benchmark/metrics.py +204 -0
- tsugite/benchmark/reports.py +420 -0
- tsugite/benchmark/utils.py +288 -0
- tsugite/builtin_agents/chat-assistant.md +53 -0
- tsugite/builtin_agents/default.md +140 -0
- tsugite/builtin_agents.py +5 -0
- tsugite/cache.py +195 -0
- tsugite/cli/__init__.py +1042 -0
- tsugite/cli/agents.py +148 -0
- tsugite/cli/attachments.py +193 -0
- tsugite/cli/benchmark.py +663 -0
- tsugite/cli/cache.py +113 -0
- tsugite/cli/config.py +272 -0
- tsugite/cli/helpers.py +534 -0
- tsugite/cli/history.py +193 -0
- tsugite/cli/init.py +387 -0
- tsugite/cli/mcp.py +193 -0
- tsugite/cli/tools.py +419 -0
- tsugite/config.py +204 -0
- tsugite/console.py +48 -0
- tsugite/constants.py +21 -0
- tsugite/core/__init__.py +19 -0
- tsugite/core/agent.py +774 -0
- tsugite/core/executor.py +300 -0
- tsugite/core/memory.py +67 -0
- tsugite/core/tools.py +271 -0
- tsugite/docker_cli.py +270 -0
- tsugite/events/__init__.py +55 -0
- tsugite/events/base.py +46 -0
- tsugite/events/bus.py +62 -0
- tsugite/events/events.py +224 -0
- tsugite/exceptions.py +40 -0
- tsugite/history/__init__.py +29 -0
- tsugite/history/index.py +210 -0
- tsugite/history/models.py +106 -0
- tsugite/history/storage.py +157 -0
- tsugite/mcp_client.py +219 -0
- tsugite/mcp_config.py +174 -0
- tsugite/md_agents.py +751 -0
- tsugite/models.py +257 -0
- tsugite/renderer.py +151 -0
- tsugite/shell_tool_config.py +265 -0
- tsugite/templates/assistant.md +14 -0
- tsugite/tools/__init__.py +265 -0
- tsugite/tools/agents.py +312 -0
- tsugite/tools/edit_strategies.py +393 -0
- tsugite/tools/fs.py +329 -0
- tsugite/tools/http.py +239 -0
- tsugite/tools/interactive.py +430 -0
- tsugite/tools/shell.py +129 -0
- tsugite/tools/shell_tools.py +214 -0
- tsugite/tools/tasks.py +339 -0
- tsugite/tsugite.py +7 -0
- tsugite/ui/__init__.py +46 -0
- tsugite/ui/base.py +638 -0
- tsugite/ui/chat.py +265 -0
- tsugite/ui/chat.tcss +92 -0
- tsugite/ui/chat_history.py +286 -0
- tsugite/ui/helpers.py +102 -0
- tsugite/ui/jsonl.py +125 -0
- tsugite/ui/live_template.py +529 -0
- tsugite/ui/plain.py +419 -0
- tsugite/ui/textual_chat.py +642 -0
- tsugite/ui/textual_handler.py +225 -0
- tsugite/ui/widgets/__init__.py +6 -0
- tsugite/ui/widgets/base_scroll_log.py +27 -0
- tsugite/ui/widgets/message_list.py +121 -0
- tsugite/ui/widgets/thought_log.py +80 -0
- tsugite/ui_context.py +90 -0
- tsugite/utils.py +367 -0
- tsugite/xdg.py +104 -0
- tsugite_cli-0.3.3.dist-info/METADATA +325 -0
- tsugite_cli-0.3.3.dist-info/RECORD +101 -0
- tsugite_cli-0.3.3.dist-info/WHEEL +4 -0
- tsugite_cli-0.3.3.dist-info/entry_points.txt +5 -0
- tsugite_cli-0.3.3.dist-info/licenses/LICENSE +235 -0
tsugite/config.py
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"""Tsugite configuration management."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Dict, List, Optional
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
8
|
+
|
|
9
|
+
from .xdg import get_xdg_config_path
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Config(BaseModel):
|
|
13
|
+
"""Tsugite configuration."""
|
|
14
|
+
|
|
15
|
+
model_config = ConfigDict(
|
|
16
|
+
extra="allow",
|
|
17
|
+
str_strip_whitespace=True,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
default_model: Optional[str] = None
|
|
21
|
+
model_aliases: Dict[str, str] = Field(default_factory=dict)
|
|
22
|
+
default_base_agent: Optional[str] = None
|
|
23
|
+
chat_theme: str = "gruvbox"
|
|
24
|
+
history_enabled: bool = True
|
|
25
|
+
history_dir: Optional[Path] = None
|
|
26
|
+
machine_name: Optional[str] = None
|
|
27
|
+
max_history_days: Optional[int] = None
|
|
28
|
+
auto_context_enabled: bool = True
|
|
29
|
+
auto_context_files: List[str] = Field(default_factory=lambda: [".tsugite/CONTEXT.md", "AGENTS.md", "CLAUDE.md"])
|
|
30
|
+
auto_context_include_global: bool = True
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def get_config_path() -> Path:
|
|
34
|
+
return get_xdg_config_path("config.json")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def load_config(path: Optional[Path] = None) -> Config:
|
|
38
|
+
"""Load Tsugite configuration from JSON file.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
path: Path to config.json file. If None, uses default path
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
Config object with loaded settings. Returns default config if file doesn't exist.
|
|
45
|
+
"""
|
|
46
|
+
if path is None:
|
|
47
|
+
path = get_config_path()
|
|
48
|
+
|
|
49
|
+
if not path.exists():
|
|
50
|
+
return Config()
|
|
51
|
+
|
|
52
|
+
try:
|
|
53
|
+
with open(path, "r", encoding="utf-8") as f:
|
|
54
|
+
data = json.load(f)
|
|
55
|
+
|
|
56
|
+
# Convert history_dir string to Path if present
|
|
57
|
+
if "history_dir" in data and data["history_dir"]:
|
|
58
|
+
data["history_dir"] = Path(data["history_dir"])
|
|
59
|
+
|
|
60
|
+
# Use Pydantic's model_validate for validation and construction
|
|
61
|
+
return Config.model_validate(data)
|
|
62
|
+
|
|
63
|
+
except json.JSONDecodeError as e:
|
|
64
|
+
print(f"Warning: Failed to parse config at {path}: {e}")
|
|
65
|
+
return Config()
|
|
66
|
+
except Exception as e:
|
|
67
|
+
print(f"Warning: Failed to load config from {path}: {e}")
|
|
68
|
+
return Config()
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def save_config(config: Config, path: Optional[Path] = None) -> None:
|
|
72
|
+
"""Save Tsugite configuration to JSON file.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
config: Config object to save
|
|
76
|
+
path: Path to config.json file. If None, uses default path
|
|
77
|
+
|
|
78
|
+
Raises:
|
|
79
|
+
IOError: If file cannot be written
|
|
80
|
+
"""
|
|
81
|
+
if path is None:
|
|
82
|
+
path = get_config_path()
|
|
83
|
+
|
|
84
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
85
|
+
|
|
86
|
+
# Use Pydantic's model_dump to serialize, excluding None values for cleaner output
|
|
87
|
+
config_data = config.model_dump(exclude_none=True, mode="json")
|
|
88
|
+
|
|
89
|
+
# Convert Path to string for JSON serialization
|
|
90
|
+
if "history_dir" in config_data and config_data["history_dir"]:
|
|
91
|
+
config_data["history_dir"] = str(config_data["history_dir"])
|
|
92
|
+
|
|
93
|
+
# Remove empty model_aliases dict for cleaner output
|
|
94
|
+
if "model_aliases" in config_data and not config_data["model_aliases"]:
|
|
95
|
+
del config_data["model_aliases"]
|
|
96
|
+
|
|
97
|
+
with open(path, "w", encoding="utf-8") as f:
|
|
98
|
+
json.dump(config_data, f, indent=2)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def set_default_model(model: str, path: Optional[Path] = None) -> None:
|
|
102
|
+
"""Set the default model in configuration.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
model: Model string (e.g., "ollama:qwen2.5-coder:7b")
|
|
106
|
+
path: Path to config.json file. If None, uses default path
|
|
107
|
+
"""
|
|
108
|
+
config = load_config(path)
|
|
109
|
+
config.default_model = model
|
|
110
|
+
save_config(config, path)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def add_model_alias(alias: str, model: str, path: Optional[Path] = None) -> None:
|
|
114
|
+
"""Add or update a model alias in configuration.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
alias: Alias name (e.g., "cheap")
|
|
118
|
+
model: Model string (e.g., "openai:gpt-4o-mini")
|
|
119
|
+
path: Path to config.json file. If None, uses default path
|
|
120
|
+
"""
|
|
121
|
+
config = load_config(path)
|
|
122
|
+
config.model_aliases[alias] = model
|
|
123
|
+
save_config(config, path)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def remove_model_alias(alias: str, path: Optional[Path] = None) -> bool:
|
|
127
|
+
"""Remove a model alias from configuration.
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
alias: Alias name to remove
|
|
131
|
+
path: Path to config.json file. If None, uses default path
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
True if alias was removed, False if it didn't exist
|
|
135
|
+
"""
|
|
136
|
+
config = load_config(path)
|
|
137
|
+
if alias in config.model_aliases:
|
|
138
|
+
del config.model_aliases[alias]
|
|
139
|
+
save_config(config, path)
|
|
140
|
+
return True
|
|
141
|
+
return False
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def get_model_alias(alias: str, path: Optional[Path] = None) -> Optional[str]:
|
|
145
|
+
"""Get the model string for an alias.
|
|
146
|
+
|
|
147
|
+
Args:
|
|
148
|
+
alias: Alias name to look up
|
|
149
|
+
path: Path to config.json file. If None, uses default path
|
|
150
|
+
|
|
151
|
+
Returns:
|
|
152
|
+
Model string if alias exists, None otherwise
|
|
153
|
+
"""
|
|
154
|
+
config = load_config(path)
|
|
155
|
+
return config.model_aliases.get(alias)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def set_default_base_agent(base_agent: Optional[str], path: Optional[Path] = None) -> None:
|
|
159
|
+
"""Set the default base agent in configuration.
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
base_agent: Base agent name (e.g., "default") or None to disable
|
|
163
|
+
path: Path to config.json file. If None, uses default path
|
|
164
|
+
"""
|
|
165
|
+
config = load_config(path)
|
|
166
|
+
config.default_base_agent = base_agent
|
|
167
|
+
save_config(config, path)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def set_chat_theme(theme: str, path: Optional[Path] = None) -> None:
|
|
171
|
+
"""Set the chat UI theme in configuration.
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
theme: Theme name (e.g., "gruvbox", "nord", "tokyo-night")
|
|
175
|
+
path: Path to config.json file. If None, uses default path
|
|
176
|
+
"""
|
|
177
|
+
config = load_config(path)
|
|
178
|
+
config.chat_theme = theme
|
|
179
|
+
save_config(config, path)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def set_auto_context_enabled(enabled: bool, path: Optional[Path] = None) -> None:
|
|
183
|
+
"""Set whether auto-context is enabled in configuration.
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
enabled: True to enable auto-context, False to disable
|
|
187
|
+
path: Path to config.json file. If None, uses default path
|
|
188
|
+
"""
|
|
189
|
+
config = load_config(path)
|
|
190
|
+
config.auto_context_enabled = enabled
|
|
191
|
+
save_config(config, path)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def get_chat_theme(path: Optional[Path] = None) -> str:
|
|
195
|
+
"""Get the chat UI theme from configuration.
|
|
196
|
+
|
|
197
|
+
Args:
|
|
198
|
+
path: Path to config.json file. If None, uses default path
|
|
199
|
+
|
|
200
|
+
Returns:
|
|
201
|
+
Theme name (defaults to "gruvbox")
|
|
202
|
+
"""
|
|
203
|
+
config = load_config(path)
|
|
204
|
+
return config.chat_theme
|
tsugite/console.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""Centralized console creation utilities."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
from rich.console import Console
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def get_stderr_console(no_color: bool = False) -> Console:
|
|
9
|
+
"""Get console for error/warning output to stderr.
|
|
10
|
+
|
|
11
|
+
Args:
|
|
12
|
+
no_color: Disable color output
|
|
13
|
+
|
|
14
|
+
Returns:
|
|
15
|
+
Console instance configured for stderr
|
|
16
|
+
"""
|
|
17
|
+
return Console(file=sys.stderr, no_color=no_color)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def get_stdout_console(no_color: bool = False, force_terminal: bool = False) -> Console:
|
|
21
|
+
"""Get console for standard output to stdout.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
no_color: Disable color output
|
|
25
|
+
force_terminal: Force terminal mode even if TTY not detected
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
Console instance configured for stdout
|
|
29
|
+
"""
|
|
30
|
+
return Console(file=sys.stdout, no_color=no_color, force_terminal=force_terminal)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def get_output_console() -> Console:
|
|
34
|
+
"""Get console for standard output (alias for get_stdout_console with no_color=True)."""
|
|
35
|
+
return get_stdout_console(no_color=True)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def get_error_console(headless: bool, console: Console) -> Console:
|
|
39
|
+
"""Get console for error output based on mode.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
headless: Whether running in headless mode
|
|
43
|
+
console: Default console to use in non-headless mode
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
Console for error output (no-color stderr in headless, console otherwise)
|
|
47
|
+
"""
|
|
48
|
+
return get_stderr_console(no_color=True) if headless else console
|
tsugite/constants.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Constants for Tsugite CLI."""
|
|
2
|
+
|
|
3
|
+
TSUGITE_LOGO_NARROW = """
|
|
4
|
+
╔╦╗╔═╗╦ ╦╔═╗╦╔╦╗╔═╗
|
|
5
|
+
║ ╚═╗║ ║║ ╦║ ║ ║╣
|
|
6
|
+
╩ ╚═╝╚═╝╚═╝╩ ╩ ╚═╝
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
TSUGITE_LOGO_WIDE = """
|
|
10
|
+
███████████ ███ █████
|
|
11
|
+
░█░░░███░░░█ ░░░ ░░███
|
|
12
|
+
░ ░███ ░ █████ █████ ████ ███████ ████ ███████ ██████
|
|
13
|
+
░███ ███░░ ░░███ ░███ ███░░███░░███ ░░░███░ ███░░███
|
|
14
|
+
░███ ░░█████ ░███ ░███ ░███ ░███ ░███ ░███ ░███████
|
|
15
|
+
░███ ░░░░███ ░███ ░███ ░███ ░███ ░███ ░███ ███░███░░░
|
|
16
|
+
█████ ██████ ░░████████░░███████ █████ ░░█████ ░░██████
|
|
17
|
+
░░░░░ ░░░░░░ ░░░░░░░░ ░░░░░███░░░░░ ░░░░░ ░░░░░░
|
|
18
|
+
███ ░███
|
|
19
|
+
░░██████ Tsugite
|
|
20
|
+
░░░░░░
|
|
21
|
+
"""
|
tsugite/core/__init__.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Core agent implementation with direct LiteLLM integration."""
|
|
2
|
+
|
|
3
|
+
from .agent import AgentResult, TsugiteAgent
|
|
4
|
+
from .executor import CodeExecutor, ExecutionResult, LocalExecutor
|
|
5
|
+
from .memory import AgentMemory, StepResult
|
|
6
|
+
from .tools import Tool, create_tool_from_function, create_tool_from_tsugite
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"TsugiteAgent",
|
|
10
|
+
"AgentResult",
|
|
11
|
+
"CodeExecutor",
|
|
12
|
+
"LocalExecutor",
|
|
13
|
+
"ExecutionResult",
|
|
14
|
+
"AgentMemory",
|
|
15
|
+
"StepResult",
|
|
16
|
+
"Tool",
|
|
17
|
+
"create_tool_from_function",
|
|
18
|
+
"create_tool_from_tsugite",
|
|
19
|
+
]
|