vtx-coding-agent 0.1.1__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.
- vtx/__init__.py +63 -0
- vtx/async_utils.py +40 -0
- vtx/builtin_skills/github/SKILL.md +139 -0
- vtx/builtin_skills/init/SKILL.md +74 -0
- vtx/builtin_skills/review/SKILL.md +73 -0
- vtx/builtin_skills/skill-builder/SKILL.md +133 -0
- vtx/cli.py +90 -0
- vtx/config.py +741 -0
- vtx/context/__init__.py +15 -0
- vtx/context/_xml.py +8 -0
- vtx/context/agent_mds.py +128 -0
- vtx/context/git.py +64 -0
- vtx/context/loader.py +41 -0
- vtx/context/skills.py +423 -0
- vtx/core/__init__.py +47 -0
- vtx/core/compaction.py +89 -0
- vtx/core/errors.py +17 -0
- vtx/core/handoff.py +51 -0
- vtx/core/scratchpad.py +54 -0
- vtx/core/types.py +197 -0
- vtx/defaults/__init__.py +0 -0
- vtx/defaults/config.yml +53 -0
- vtx/diff_display.py +12 -0
- vtx/events.py +224 -0
- vtx/gh_cli.py +82 -0
- vtx/git_branch.py +90 -0
- vtx/headless.py +127 -0
- vtx/llm/__init__.py +93 -0
- vtx/llm/base.py +217 -0
- vtx/llm/context_length.py +150 -0
- vtx/llm/dynamic_models.py +735 -0
- vtx/llm/model_fetcher.py +279 -0
- vtx/llm/models.py +78 -0
- vtx/llm/oauth/__init__.py +59 -0
- vtx/llm/oauth/copilot.py +358 -0
- vtx/llm/oauth/dynamic.py +236 -0
- vtx/llm/oauth/openai.py +400 -0
- vtx/llm/phase_parser.py +270 -0
- vtx/llm/provider.yaml +280 -0
- vtx/llm/provider_catalog.py +230 -0
- vtx/llm/providers/__init__.py +45 -0
- vtx/llm/providers/anthropic_sdk.py +256 -0
- vtx/llm/providers/mock.py +249 -0
- vtx/llm/providers/openai_sdk.py +246 -0
- vtx/llm/providers/sanitize.py +14 -0
- vtx/llm/sdk/__init__.py +13 -0
- vtx/llm/sdk/anthropic.py +382 -0
- vtx/llm/sdk/base.py +82 -0
- vtx/llm/sdk/openai.py +344 -0
- vtx/llm/tool_parser.py +161 -0
- vtx/loop.py +272 -0
- vtx/notify.py +109 -0
- vtx/permissions.py +114 -0
- vtx/prompts/__init__.py +45 -0
- vtx/prompts/builder.py +86 -0
- vtx/prompts/env.py +58 -0
- vtx/prompts/identity.py +166 -0
- vtx/prompts/tooling.py +36 -0
- vtx/py.typed +0 -0
- vtx/runtime.py +580 -0
- vtx/session.py +868 -0
- vtx/sounds/completion.wav +0 -0
- vtx/sounds/error.wav +0 -0
- vtx/sounds/permission.wav +0 -0
- vtx/themes.py +1104 -0
- vtx/tools/__init__.py +68 -0
- vtx/tools/_read_image.py +106 -0
- vtx/tools/_tool_utils.py +90 -0
- vtx/tools/base.py +36 -0
- vtx/tools/bash.py +371 -0
- vtx/tools/edit.py +261 -0
- vtx/tools/find.py +132 -0
- vtx/tools/read.py +238 -0
- vtx/tools/skill.py +278 -0
- vtx/tools/web.py +238 -0
- vtx/tools/write.py +88 -0
- vtx/tools_manager.py +216 -0
- vtx/turn.py +789 -0
- vtx/ui/__init__.py +0 -0
- vtx/ui/agent_runner.py +417 -0
- vtx/ui/app.py +665 -0
- vtx/ui/app_protocol.py +29 -0
- vtx/ui/autocomplete.py +440 -0
- vtx/ui/blocks.py +735 -0
- vtx/ui/chat.py +613 -0
- vtx/ui/clipboard.py +59 -0
- vtx/ui/commands/__init__.py +100 -0
- vtx/ui/commands/auth.py +306 -0
- vtx/ui/commands/base.py +122 -0
- vtx/ui/commands/models.py +144 -0
- vtx/ui/commands/sessions.py +388 -0
- vtx/ui/commands/settings.py +286 -0
- vtx/ui/completion_ui.py +313 -0
- vtx/ui/export.py +703 -0
- vtx/ui/floating_list.py +370 -0
- vtx/ui/formatting.py +287 -0
- vtx/ui/input.py +760 -0
- vtx/ui/latex.py +349 -0
- vtx/ui/launch.py +108 -0
- vtx/ui/path_complete.py +228 -0
- vtx/ui/prompt_history.py +102 -0
- vtx/ui/queue_ui.py +141 -0
- vtx/ui/selection_mode.py +18 -0
- vtx/ui/session_ui.py +235 -0
- vtx/ui/startup.py +124 -0
- vtx/ui/styles.py +327 -0
- vtx/ui/tool_output.py +34 -0
- vtx/ui/tree.py +437 -0
- vtx/ui/welcome.py +51 -0
- vtx/ui/widgets.py +558 -0
- vtx/update_check.py +49 -0
- vtx/version.py +22 -0
- vtx_coding_agent-0.1.1.dist-info/METADATA +259 -0
- vtx_coding_agent-0.1.1.dist-info/RECORD +117 -0
- vtx_coding_agent-0.1.1.dist-info/WHEEL +4 -0
- vtx_coding_agent-0.1.1.dist-info/entry_points.txt +2 -0
- vtx_coding_agent-0.1.1.dist-info/licenses/LICENSE +201 -0
vtx/ui/styles.py
ADDED
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
"""TUI styles for vtx."""
|
|
2
|
+
|
|
3
|
+
from vtx import config
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def _blend_hex(base: str, overlay: str, overlay_weight: float) -> str:
|
|
7
|
+
"""Blend two hex colors, biasing toward the base color."""
|
|
8
|
+
base_rgb = tuple(int(base[i : i + 2], 16) for i in (1, 3, 5))
|
|
9
|
+
overlay_rgb = tuple(int(overlay[i : i + 2], 16) for i in (1, 3, 5))
|
|
10
|
+
channels = tuple(
|
|
11
|
+
round((base_channel * (1 - overlay_weight)) + (overlay_channel * overlay_weight))
|
|
12
|
+
for base_channel, overlay_channel in zip(base_rgb, overlay_rgb, strict=True)
|
|
13
|
+
)
|
|
14
|
+
return f"#{channels[0]:02x}{channels[1]:02x}{channels[2]:02x}"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def get_styles() -> str:
|
|
18
|
+
colors = config.ui.colors
|
|
19
|
+
approval_bg = _blend_hex(colors.bg, colors.accent, overlay_weight=0.05)
|
|
20
|
+
shell_bg = _blend_hex(colors.editor, colors.success, overlay_weight=0.15)
|
|
21
|
+
thinking_medium_bg = _blend_hex(colors.editor, colors.accent, overlay_weight=0.08)
|
|
22
|
+
thinking_high_bg = _blend_hex(colors.editor, colors.accent, overlay_weight=0.18)
|
|
23
|
+
thinking_xhigh_bg = _blend_hex(colors.editor, colors.badge.label, overlay_weight=0.18)
|
|
24
|
+
|
|
25
|
+
return f"""
|
|
26
|
+
Screen {{
|
|
27
|
+
layout: grid;
|
|
28
|
+
grid-size: 1;
|
|
29
|
+
grid-rows: 1fr auto auto auto auto auto;
|
|
30
|
+
background: transparent;
|
|
31
|
+
color: {colors.fg};
|
|
32
|
+
}}
|
|
33
|
+
|
|
34
|
+
#chat-log {{
|
|
35
|
+
height: 100%;
|
|
36
|
+
padding: 0 0 1 0;
|
|
37
|
+
scrollbar-size: 0 0;
|
|
38
|
+
align-vertical: bottom;
|
|
39
|
+
background: transparent;
|
|
40
|
+
color: {colors.fg};
|
|
41
|
+
}}
|
|
42
|
+
|
|
43
|
+
/* Thinking block - dim italic */
|
|
44
|
+
.thinking-block {{
|
|
45
|
+
color: {colors.dim};
|
|
46
|
+
text-style: italic;
|
|
47
|
+
padding: 0 1 0 1;
|
|
48
|
+
margin: 1 0 0 0;
|
|
49
|
+
}}
|
|
50
|
+
|
|
51
|
+
.thinking-block.-hidden {{
|
|
52
|
+
display: none;
|
|
53
|
+
height: 0;
|
|
54
|
+
margin: 0;
|
|
55
|
+
border: none;
|
|
56
|
+
}}
|
|
57
|
+
|
|
58
|
+
#thinking-content {{
|
|
59
|
+
color: {colors.dim};
|
|
60
|
+
text-style: italic;
|
|
61
|
+
}}
|
|
62
|
+
|
|
63
|
+
/* Content block */
|
|
64
|
+
.content-block {{
|
|
65
|
+
padding: 0 1;
|
|
66
|
+
margin-top: 1;
|
|
67
|
+
}}
|
|
68
|
+
|
|
69
|
+
/* Ensure text wraps in all blocks */
|
|
70
|
+
.thinking-block Label,
|
|
71
|
+
.content-block Label,
|
|
72
|
+
.user-block Label,
|
|
73
|
+
.update-available-block Label,
|
|
74
|
+
.launch-warnings-block Label,
|
|
75
|
+
.tool-block Label,
|
|
76
|
+
.handoff-link-block Label {{
|
|
77
|
+
width: 100%;
|
|
78
|
+
}}
|
|
79
|
+
|
|
80
|
+
/* User message */
|
|
81
|
+
.user-block {{
|
|
82
|
+
padding: 1 1;
|
|
83
|
+
margin: 1 0 0 0;
|
|
84
|
+
background: {colors.editor};
|
|
85
|
+
}}
|
|
86
|
+
|
|
87
|
+
.user-block.skill-trigger-message {{
|
|
88
|
+
background: {colors.editor};
|
|
89
|
+
}}
|
|
90
|
+
|
|
91
|
+
/* Update available message */
|
|
92
|
+
.update-available-block {{
|
|
93
|
+
padding: 0 1;
|
|
94
|
+
margin: 1 0 0 0;
|
|
95
|
+
border-top: solid {colors.notice};
|
|
96
|
+
border-bottom: solid {colors.notice};
|
|
97
|
+
}}
|
|
98
|
+
|
|
99
|
+
/* Launch warnings */
|
|
100
|
+
.launch-warnings-block {{
|
|
101
|
+
padding: 0 1;
|
|
102
|
+
margin: 1 0 0 0;
|
|
103
|
+
border-top: solid {colors.notice};
|
|
104
|
+
border-bottom: solid {colors.notice};
|
|
105
|
+
}}
|
|
106
|
+
|
|
107
|
+
/* Session info */
|
|
108
|
+
.session-info {{
|
|
109
|
+
padding: 1;
|
|
110
|
+
}}
|
|
111
|
+
|
|
112
|
+
/* Tool block */
|
|
113
|
+
.tool-block {{
|
|
114
|
+
padding: 0 1;
|
|
115
|
+
margin-top: 1;
|
|
116
|
+
background: transparent;
|
|
117
|
+
}}
|
|
118
|
+
|
|
119
|
+
.tool-block.-compact {{
|
|
120
|
+
margin-top: 0;
|
|
121
|
+
}}
|
|
122
|
+
|
|
123
|
+
.tool-block.-pending,
|
|
124
|
+
.tool-block.-success,
|
|
125
|
+
.tool-block.-error {{
|
|
126
|
+
background: transparent;
|
|
127
|
+
color: {colors.dim};
|
|
128
|
+
border: none;
|
|
129
|
+
}}
|
|
130
|
+
|
|
131
|
+
.tool-block.-approval {{
|
|
132
|
+
background: {approval_bg};
|
|
133
|
+
color: {colors.dim};
|
|
134
|
+
border-left: outer {colors.accent};
|
|
135
|
+
margin: 1 0 1 1;
|
|
136
|
+
padding: 1 1;
|
|
137
|
+
}}
|
|
138
|
+
|
|
139
|
+
.tool-block.-approval #tool-output {{
|
|
140
|
+
padding: 1 0 0 0;
|
|
141
|
+
}}
|
|
142
|
+
|
|
143
|
+
#tool-header {{
|
|
144
|
+
text-style: none;
|
|
145
|
+
}}
|
|
146
|
+
|
|
147
|
+
#tool-output,
|
|
148
|
+
.tool-output {{
|
|
149
|
+
color: {colors.dim};
|
|
150
|
+
padding: 0 0 0 2;
|
|
151
|
+
}}
|
|
152
|
+
|
|
153
|
+
#tool-output.-diff-output {{
|
|
154
|
+
text-wrap: nowrap;
|
|
155
|
+
text-overflow: clip;
|
|
156
|
+
}}
|
|
157
|
+
|
|
158
|
+
.tool-block.-with-details {{
|
|
159
|
+
padding: 0 1;
|
|
160
|
+
}}
|
|
161
|
+
|
|
162
|
+
#tool-output.-hidden {{
|
|
163
|
+
display: none;
|
|
164
|
+
height: 0;
|
|
165
|
+
}}
|
|
166
|
+
|
|
167
|
+
/* Compaction message */
|
|
168
|
+
.compaction-message {{
|
|
169
|
+
background: {colors.panel};
|
|
170
|
+
padding: 1 1;
|
|
171
|
+
margin-top: 1;
|
|
172
|
+
width: 100%;
|
|
173
|
+
}}
|
|
174
|
+
|
|
175
|
+
/* Handoff link */
|
|
176
|
+
.handoff-link-block {{
|
|
177
|
+
background: {colors.panel};
|
|
178
|
+
padding: 1 1;
|
|
179
|
+
margin: 1 0 0 0;
|
|
180
|
+
width: 100%;
|
|
181
|
+
}}
|
|
182
|
+
|
|
183
|
+
/* Aborted message */
|
|
184
|
+
.aborted-message {{
|
|
185
|
+
padding: 0 1;
|
|
186
|
+
margin-top: 1;
|
|
187
|
+
}}
|
|
188
|
+
|
|
189
|
+
/* Info message */
|
|
190
|
+
.info-message {{
|
|
191
|
+
padding: 0 1;
|
|
192
|
+
margin-top: 1;
|
|
193
|
+
}}
|
|
194
|
+
|
|
195
|
+
/* Loaded resources should not add extra top margin */
|
|
196
|
+
.info-message.loaded-resources {{
|
|
197
|
+
margin-top: 0;
|
|
198
|
+
}}
|
|
199
|
+
|
|
200
|
+
/* Queue display - shown above status line when messages are queued */
|
|
201
|
+
#queue-display {{
|
|
202
|
+
height: auto;
|
|
203
|
+
padding: 0 1 1 1;
|
|
204
|
+
}}
|
|
205
|
+
|
|
206
|
+
#queue-display.-hidden {{
|
|
207
|
+
display: none;
|
|
208
|
+
}}
|
|
209
|
+
|
|
210
|
+
#queue-content {{
|
|
211
|
+
color: {colors.dim};
|
|
212
|
+
width: 100%;
|
|
213
|
+
}}
|
|
214
|
+
|
|
215
|
+
/* Status line - vtx style with spinner */
|
|
216
|
+
.status-line {{
|
|
217
|
+
height: auto;
|
|
218
|
+
min-height: 1;
|
|
219
|
+
padding: 0 1;
|
|
220
|
+
color: $warning;
|
|
221
|
+
}}
|
|
222
|
+
|
|
223
|
+
#status-text {{
|
|
224
|
+
color: {colors.dim};
|
|
225
|
+
width: 1fr;
|
|
226
|
+
}}
|
|
227
|
+
|
|
228
|
+
#exit-hint {{
|
|
229
|
+
color: {colors.dim};
|
|
230
|
+
width: auto;
|
|
231
|
+
}}
|
|
232
|
+
|
|
233
|
+
/* Input area */
|
|
234
|
+
#input-box {{
|
|
235
|
+
background: {colors.editor};
|
|
236
|
+
border-top: solid {colors.editor};
|
|
237
|
+
border-bottom: solid {colors.editor};
|
|
238
|
+
border-title-color: {colors.dim};
|
|
239
|
+
border-subtitle-color: {colors.dim};
|
|
240
|
+
}}
|
|
241
|
+
|
|
242
|
+
#input-prefix {{
|
|
243
|
+
color: {colors.fg};
|
|
244
|
+
text-style: bold;
|
|
245
|
+
}}
|
|
246
|
+
|
|
247
|
+
#input-box.-thinking-none,
|
|
248
|
+
#input-box.-thinking-minimal,
|
|
249
|
+
#input-box.-thinking-low {{
|
|
250
|
+
background: {colors.editor};
|
|
251
|
+
border-top: solid {colors.editor};
|
|
252
|
+
border-bottom: solid {colors.editor};
|
|
253
|
+
}}
|
|
254
|
+
|
|
255
|
+
#input-box.-thinking-medium {{
|
|
256
|
+
background: {thinking_medium_bg};
|
|
257
|
+
border-top: solid {thinking_medium_bg};
|
|
258
|
+
border-bottom: solid {thinking_medium_bg};
|
|
259
|
+
}}
|
|
260
|
+
|
|
261
|
+
#input-box.-thinking-high {{
|
|
262
|
+
background: {thinking_high_bg};
|
|
263
|
+
border-top: solid {thinking_high_bg};
|
|
264
|
+
border-bottom: solid {thinking_high_bg};
|
|
265
|
+
}}
|
|
266
|
+
|
|
267
|
+
#input-box.-thinking-xhigh {{
|
|
268
|
+
background: {thinking_xhigh_bg};
|
|
269
|
+
border-top: solid {thinking_xhigh_bg};
|
|
270
|
+
border-bottom: solid {thinking_xhigh_bg};
|
|
271
|
+
}}
|
|
272
|
+
|
|
273
|
+
#input-box.-shell-command {{
|
|
274
|
+
background: {shell_bg};
|
|
275
|
+
border-top: solid {shell_bg};
|
|
276
|
+
border-bottom: solid {shell_bg};
|
|
277
|
+
}}
|
|
278
|
+
|
|
279
|
+
#input-box.-shell-command .input-textarea {{
|
|
280
|
+
color: {colors.success};
|
|
281
|
+
}}
|
|
282
|
+
|
|
283
|
+
/* Completion list - between input and info bar */
|
|
284
|
+
#completion-list {{
|
|
285
|
+
height: auto;
|
|
286
|
+
padding: 0 1;
|
|
287
|
+
}}
|
|
288
|
+
|
|
289
|
+
/* Info bar - vtx style tmux-like bottom bar with two rows */
|
|
290
|
+
.info-bar {{
|
|
291
|
+
height: 2;
|
|
292
|
+
color: {colors.dim};
|
|
293
|
+
}}
|
|
294
|
+
|
|
295
|
+
.info-bar.-completion-hidden {{
|
|
296
|
+
display: none;
|
|
297
|
+
height: 0;
|
|
298
|
+
}}
|
|
299
|
+
|
|
300
|
+
#info-row-1, #info-row-2 {{
|
|
301
|
+
height: 1;
|
|
302
|
+
}}
|
|
303
|
+
|
|
304
|
+
#info-cwd {{
|
|
305
|
+
width: 1fr;
|
|
306
|
+
padding: 0 1;
|
|
307
|
+
}}
|
|
308
|
+
|
|
309
|
+
#info-row2-left {{
|
|
310
|
+
width: 1fr;
|
|
311
|
+
padding: 0 1;
|
|
312
|
+
}}
|
|
313
|
+
|
|
314
|
+
#info-row1-right, #info-row2-right {{
|
|
315
|
+
width: auto;
|
|
316
|
+
padding: 0 1;
|
|
317
|
+
text-align: right;
|
|
318
|
+
}}
|
|
319
|
+
|
|
320
|
+
/* Notifications */
|
|
321
|
+
Notification {{
|
|
322
|
+
layer: notification;
|
|
323
|
+
}}
|
|
324
|
+
"""
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
STYLES = get_styles()
|
vtx/ui/tool_output.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from rich.markup import escape
|
|
2
|
+
from rich.text import Text
|
|
3
|
+
|
|
4
|
+
from vtx import config
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def format_expand_hint(hidden_lines: int) -> Text:
|
|
8
|
+
colors = config.ui.colors
|
|
9
|
+
text = Text()
|
|
10
|
+
text.append(f"... ({hidden_lines} lines hidden • ", style=colors.dim)
|
|
11
|
+
text.append("ctrl+o", style=colors.muted)
|
|
12
|
+
text.append(" to expand)", style=colors.dim)
|
|
13
|
+
return text
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def escape_tool_output_text(text: str) -> str:
|
|
17
|
+
return "\n".join(escape(line) for line in text.split("\n"))
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def truncate_tool_output_text(
|
|
21
|
+
text: str, max_lines: int = 5, escape_lines: bool = True
|
|
22
|
+
) -> tuple[str, bool]:
|
|
23
|
+
if not text:
|
|
24
|
+
return text, False
|
|
25
|
+
|
|
26
|
+
lines = text.split("\n")
|
|
27
|
+
if len(lines) <= max_lines:
|
|
28
|
+
return text, False
|
|
29
|
+
|
|
30
|
+
hidden = len(lines) - max_lines
|
|
31
|
+
visible = [escape(line) if escape_lines else line for line in lines[:max_lines]]
|
|
32
|
+
hint = format_expand_hint(hidden_lines=hidden)
|
|
33
|
+
visible.append(hint.markup)
|
|
34
|
+
return "\n".join(visible), True
|