tau-tui 0.2.0__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.
- tau_tui-0.2.0/.gitignore +25 -0
- tau_tui-0.2.0/LICENSE +21 -0
- tau_tui-0.2.0/PKG-INFO +274 -0
- tau_tui-0.2.0/README.md +247 -0
- tau_tui-0.2.0/pyproject.toml +36 -0
- tau_tui-0.2.0/src/tau_tui/__init__.py +35 -0
- tau_tui-0.2.0/src/tau_tui/app.py +947 -0
- tau_tui-0.2.0/src/tau_tui/approval.py +230 -0
- tau_tui-0.2.0/src/tau_tui/brand.py +247 -0
- tau_tui-0.2.0/src/tau_tui/cli.py +59 -0
- tau_tui-0.2.0/src/tau_tui/commands.py +182 -0
- tau_tui-0.2.0/src/tau_tui/composer.py +391 -0
- tau_tui-0.2.0/src/tau_tui/events.py +228 -0
- tau_tui-0.2.0/src/tau_tui/header.py +113 -0
- tau_tui-0.2.0/src/tau_tui/history.py +174 -0
- tau_tui-0.2.0/src/tau_tui/keys.py +112 -0
- tau_tui-0.2.0/src/tau_tui/sessions.py +261 -0
- tau_tui-0.2.0/src/tau_tui/strings.py +144 -0
- tau_tui-0.2.0/src/tau_tui/theme.py +252 -0
- tau_tui-0.2.0/src/tau_tui/tools.py +416 -0
- tau_tui-0.2.0/src/tau_tui/transcript.py +821 -0
- tau_tui-0.2.0/tests/__snapshots__/test_snapshots/test_approval_80x24.raw +167 -0
- tau_tui-0.2.0/tests/__snapshots__/test_snapshots/test_busy_80x24.raw +164 -0
- tau_tui-0.2.0/tests/__snapshots__/test_snapshots/test_chat_80x24.raw +168 -0
- tau_tui-0.2.0/tests/__snapshots__/test_snapshots/test_events_50x24.raw +164 -0
- tau_tui-0.2.0/tests/__snapshots__/test_snapshots/test_menu_80x24.raw +163 -0
- tau_tui-0.2.0/tests/__snapshots__/test_snapshots/test_picker_80x24.raw +164 -0
- tau_tui-0.2.0/tests/__snapshots__/test_snapshots/test_short_80x18.raw +137 -0
- tau_tui-0.2.0/tests/__snapshots__/test_snapshots/test_ultra_160x45.raw +256 -0
- tau_tui-0.2.0/tests/__snapshots__/test_snapshots/test_welcome_50x24.raw +198 -0
- tau_tui-0.2.0/tests/__snapshots__/test_snapshots/test_welcome_80x24.raw +222 -0
- tau_tui-0.2.0/tests/__snapshots__/test_snapshots/test_welcome_tr_80x24.raw +222 -0
- tau_tui-0.2.0/tests/__snapshots__/test_snapshots/test_wide_100x30.raw +192 -0
- tau_tui-0.2.0/tests/conftest.py +8 -0
- tau_tui-0.2.0/tests/fixtures/mark_16.txt +8 -0
- tau_tui-0.2.0/tests/fixtures/mark_24.txt +12 -0
- tau_tui-0.2.0/tests/helpers.py +250 -0
- tau_tui-0.2.0/tests/test_app.py +469 -0
- tau_tui-0.2.0/tests/test_approval.py +221 -0
- tau_tui-0.2.0/tests/test_brand.py +107 -0
- tau_tui-0.2.0/tests/test_cancel.py +62 -0
- tau_tui-0.2.0/tests/test_cli.py +174 -0
- tau_tui-0.2.0/tests/test_commands.py +552 -0
- tau_tui-0.2.0/tests/test_composer.py +237 -0
- tau_tui-0.2.0/tests/test_events.py +73 -0
- tau_tui-0.2.0/tests/test_history.py +241 -0
- tau_tui-0.2.0/tests/test_sessions.py +329 -0
- tau_tui-0.2.0/tests/test_snapshots.py +242 -0
- tau_tui-0.2.0/tests/test_transcript.py +380 -0
tau_tui-0.2.0/.gitignore
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
.DS_Store
|
|
2
|
+
.env
|
|
3
|
+
.env.*
|
|
4
|
+
!.env.example
|
|
5
|
+
*.local.env
|
|
6
|
+
__pycache__/
|
|
7
|
+
*.py[cod]
|
|
8
|
+
|
|
9
|
+
# Python / uv
|
|
10
|
+
.venv/
|
|
11
|
+
.pytest_cache/
|
|
12
|
+
.ruff_cache/
|
|
13
|
+
dist/
|
|
14
|
+
build/
|
|
15
|
+
*.egg-info/
|
|
16
|
+
|
|
17
|
+
# Personal facts about the user stay local (persona/user.example.md is the tracked template)
|
|
18
|
+
persona/user.md
|
|
19
|
+
|
|
20
|
+
# Tools tau wrote itself, waiting for approval
|
|
21
|
+
tools/_pending/*
|
|
22
|
+
!tools/_pending/.gitkeep
|
|
23
|
+
|
|
24
|
+
# MkDocs build output
|
|
25
|
+
site/
|
tau_tui-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 fport
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
tau_tui-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: tau-tui
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Textual terminal UI for tau: streamed markdown replies, tool cards, live events and in-TUI approvals.
|
|
5
|
+
Project-URL: Homepage, https://github.com/fport/tau
|
|
6
|
+
Project-URL: Documentation, https://docs.tau.getporti.com
|
|
7
|
+
Project-URL: Issues, https://github.com/fport/tau/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/fport/tau/releases
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: agent,assistant,cli,tau,terminal,textual,tui
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: MacOS
|
|
17
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Home Automation
|
|
21
|
+
Requires-Python: >=3.12
|
|
22
|
+
Requires-Dist: click>=8.1
|
|
23
|
+
Requires-Dist: rich>=13
|
|
24
|
+
Requires-Dist: tau-core>=0.1.0
|
|
25
|
+
Requires-Dist: textual<9,>=8.2
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# tau-tui
|
|
29
|
+
|
|
30
|
+
The [Textual](https://textual.textualize.io/) terminal interface for tau: replies stream in as
|
|
31
|
+
markdown, tool calls are cards with their tier, approval and result, agent events show in a live
|
|
32
|
+
pane, tier-2 calls ask in a modal dialog, and the brand's τ mark opens every new session. One row
|
|
33
|
+
of chrome at the top, at most four at the bottom, so 80x24 over mosh on a phone stays usable.
|
|
34
|
+
|
|
35
|
+
It is its own distribution (`tau-tui`, import `tau_tui`), uses only the public API of `tau-core`
|
|
36
|
+
and plugs into the `tau` command through the `tau.commands` entry point (`tui`):
|
|
37
|
+
|
|
38
|
+
```sh
|
|
39
|
+
pip install tau-tui # or, in this repo: uv sync --all-packages --group dev
|
|
40
|
+
tau tui # new session
|
|
41
|
+
tau tui --resume # continue the most recent earlier session
|
|
42
|
+
tau tui --resume ID # continue a given session
|
|
43
|
+
tau --home /path/to/tau --lang tr tui --profile travel
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Configuration comes from `tau.toml` and `.env` like every other channel (`TAU_HOME`,
|
|
47
|
+
`TAU_DATA_DIR`, `TAU_PROFILE`, `TAU_LANG`). Missing credentials, a broken config or an unknown
|
|
48
|
+
session end the command with a plain English message and exit code 2 before the interface opens.
|
|
49
|
+
The chrome (hints, labels, notices, the approval dialog) follows `[ui] language` (`en` or `tr`);
|
|
50
|
+
`/lang` switches it for one run.
|
|
51
|
+
|
|
52
|
+
## Layout
|
|
53
|
+
|
|
54
|
+
Four bands by width (`-phone` < 60 cols, `-narrow` 60–99, `-wide` 100–139, `-ultra` ≥ 140) and
|
|
55
|
+
three by height (`-short` < 20 rows, `-mid`, `-tall` ≥ 30). The header is one row, the composer a
|
|
56
|
+
rounded box of one to five text rows, the hint bar one row.
|
|
57
|
+
|
|
58
|
+
80x24 (`-narrow`), idle after a two-tool turn:
|
|
59
|
+
|
|
60
|
+
```text
|
|
61
|
+
τ tau home · 20260929-120000-abcd brain · en
|
|
62
|
+
› Salon lambası açık mı? Açıksa kapat.
|
|
63
|
+
● get_light_state(room="salon") tier 0 · 38ms
|
|
64
|
+
⎿ {"on": true, "brightness": 80}
|
|
65
|
+
● set_light(room="salon", on=false) tier 2 · approved · 412ms
|
|
66
|
+
⎿ Light turned off
|
|
67
|
+
τ Salon lambası kapatıldı.
|
|
68
|
+
• Önceki durum: açık, %80 parlaklık
|
|
69
|
+
✓ Worked for 4s · 2 tool calls · ctrl+o expand
|
|
70
|
+
╭──────────────────────────────────────────────────────────────────────────────╮
|
|
71
|
+
│ ❯ Type a message… │
|
|
72
|
+
╰──────────────────────────────────────────────────────────────────────────────╯
|
|
73
|
+
? shortcuts · / commands · ctrl+j newline ctrl+q quit
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
100x30 (`-wide`): the events pane docks on the right (34 cols; 40 at `-ultra`), `ctrl+e` hides
|
|
77
|
+
it. 160x45 (`-ultra`): a read-only session sidebar (28 cols) on the left; clicking a row resumes.
|
|
78
|
+
|
|
79
|
+
```text
|
|
80
|
+
τ tau home · 20260929-120000-abcd brain · 7 tools · en
|
|
81
|
+
› Salon lambası açık mı? Açıksa kapat. │ Events · 5
|
|
82
|
+
● get_light_state(room="salon") tier 0 · 38ms │ 12:00:01 ● get_light_state
|
|
83
|
+
⎿ {"on": true, "brightness": 80} │ tier 0 · ok · 38ms
|
|
84
|
+
τ Salon lambası kapatıldı. │ 12:00:02 ◆ approval set_light
|
|
85
|
+
│ tier 2 → approved (tui)
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
80x30 (`-narrow -tall`): an 8-row events strip under the transcript. Below 100 cols `ctrl+e` /
|
|
89
|
+
F4 swaps the transcript for the events log in the same body; the composer stays live and Esc
|
|
90
|
+
returns to the chat.
|
|
91
|
+
|
|
92
|
+
50x24 (`-phone`, Termius over mosh): the header drops the session id (`/session` shows it),
|
|
93
|
+
tool badges move to the result line, the composer has no border, hints say `^j`, the welcome
|
|
94
|
+
card shows the 16-column mark, replies stream as plain text and settle into markdown when the
|
|
95
|
+
segment ends, and the approval dialog is full width with stacked buttons.
|
|
96
|
+
|
|
97
|
+
```text
|
|
98
|
+
τ tau · home en
|
|
99
|
+
› Salon lambası açık mı? Açıksa kapat.
|
|
100
|
+
○ set_light(room="salon", on=false)
|
|
101
|
+
⎿ tier 2 · running
|
|
102
|
+
✻ Running set_light…
|
|
103
|
+
❯
|
|
104
|
+
esc cancel · ^j newline busy
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
`-short` (< 20 rows): no hint row, the welcome mark is hidden, the composer grows to two rows.
|
|
108
|
+
|
|
109
|
+
## Vocabulary
|
|
110
|
+
|
|
111
|
+
Every glyph is one cell wide and none is an emoji-presentation code point, so phone terminals
|
|
112
|
+
draw them at the right width: `›` you · `τ` tau · `●` finished tool call · `○` running or waiting
|
|
113
|
+
for approval · `⎿` result line · `✻` busy verb / reasoning · `✓ ◆ ✗` turn footer (finished /
|
|
114
|
+
limit, cancelled, interrupted / error) · `◆` needs you · `▸` notice · `❯` prompt.
|
|
115
|
+
|
|
116
|
+
State colours (deliberate; no green and no red in the palette, shapes double the colours):
|
|
117
|
+
**ok / success = Flare**, **error = Signal**, **rejected = Ember**, **running / pending = Core at
|
|
118
|
+
55 %**, **attention = Flare**, notice = Ember. Rich renderables (the events pane) cannot take a TCSS
|
|
119
|
+
alpha, so they use the literal blends `theme.TEXT_MUTED` (Core over Void at 60 %, the live clock and
|
|
120
|
+
the running dot) and `theme.TEXT_DISABLED` (38 %, replayed clocks). The header `τ` breathes while the welcome card is
|
|
121
|
+
on screen, runs the comet while a turn is busy and is static Ember otherwise; nothing else in
|
|
122
|
+
the UI moves. The composer border is Ember, Signal while busy and Flare while an approval waits.
|
|
123
|
+
|
|
124
|
+
## Keys and commands
|
|
125
|
+
|
|
126
|
+
| Key | Effect |
|
|
127
|
+
|---|---|
|
|
128
|
+
| Enter | send (or accept the highlighted menu row); while tau answers the text stays and a toast says so |
|
|
129
|
+
| ctrl+j | newline (shift+enter too where the terminal delivers it) |
|
|
130
|
+
| Esc | close the menu · cancel the running turn · leave the events view · scroll back to the bottom |
|
|
131
|
+
| ctrl+e / F4 | events: toggle the column (≥ 100 cols) or the view (< 100 cols) |
|
|
132
|
+
| ctrl+r / F2 | session picker (Enter resumes, `n` new, Esc closes) |
|
|
133
|
+
| ctrl+n | new session |
|
|
134
|
+
| ctrl+t / F3 | `/tools` |
|
|
135
|
+
| ctrl+o / F6 | expand or collapse the cards of the last turn: the arguments as JSON and the full result (a click toggles any card) |
|
|
136
|
+
| PageUp / PageDown, F7 / F8 | scroll the transcript; PageUp at the top of a folded history loads 20 older turns |
|
|
137
|
+
| F9 | back to the bottom |
|
|
138
|
+
| ↑ / ↓ | recall sent prompts (single-line composer); navigate the menu while it is open |
|
|
139
|
+
| `?` or F1 | help (only in an empty composer; `?x` is sent as a message) |
|
|
140
|
+
| ctrl+q | quit (ctrl+c copies a selection in the composer; without one it only says how to quit) |
|
|
141
|
+
|
|
142
|
+
Typing `/` into an empty composer opens a fuzzy menu of the slash commands (`↑↓` pick, Tab
|
|
143
|
+
complete, Enter run, Esc close). An exact name ranks first, and a command typed in full runs as
|
|
144
|
+
typed even when the menu has not caught up with a burst of keys (a paste, mosh), so `/session`
|
|
145
|
+
never runs `/sessions`. The commands are the shared spec of `tau_core.commands`, with
|
|
146
|
+
the same wording as `tau chat`:
|
|
147
|
+
|
|
148
|
+
| command | effect |
|
|
149
|
+
|---|---|
|
|
150
|
+
| `/help`, `/tools`, `/sessions`, `/session`, `/lang en\|tr` | as in `tau chat` |
|
|
151
|
+
| `/resume [n\|id]`, `/new` | switch sessions (see below) |
|
|
152
|
+
| `/compact [focus]` | summarize this session with its own model and continue in a new session seeded with the summary; the old session is kept |
|
|
153
|
+
| `/clear` | empty the transcript (turns, welcome card, info cards, the fold); the session and the agent go on, `/resume` or `--resume` show the history again |
|
|
154
|
+
| `/status` | an info card: profile, session, model, context, tools, language, versions, hub |
|
|
155
|
+
| `/model [role]` | list the roles of `tau.toml`, or switch this session to one; the header follows |
|
|
156
|
+
| `/quit` (alias `/exit`) | leave |
|
|
157
|
+
|
|
158
|
+
The Turkish aliases (`/devam`, `/çık`, `/özet`, `/temizle`, `/durum`, `/rol`, …) keep working
|
|
159
|
+
but are never listed; the menu shows the twelve canonical names and every row is on screen at
|
|
160
|
+
80x24 (in the `-short` band it scrolls). `/new`, `/resume`, `/compact`, `/clear` and
|
|
161
|
+
`/model <role>` wait for the current turn (a toast says so); `/status` and `/model` without an
|
|
162
|
+
argument run any time.
|
|
163
|
+
|
|
164
|
+
## Approvals
|
|
165
|
+
|
|
166
|
+
`TuiApprover` (`name = "tui"`) is the channel's `Approver`. A tier-2 call opens `ApprovalScreen`:
|
|
167
|
+
tool, effect, the arguments as pretty JSON and the buttons **Approve** / **Reject**.
|
|
168
|
+
|
|
169
|
+
- `y` or `e` approves, `n`, `h` or Esc rejects; focus starts on **Reject**, so Enter rejects, and
|
|
170
|
+
Tab only moves between the two buttons (PageUp/PageDown scroll the arguments). The keys work in
|
|
171
|
+
both languages whatever the UI setting.
|
|
172
|
+
- `y`/`e` and **Approve** only work 0.5 s after the dialog opens (the hint reads `(arming…)`
|
|
173
|
+
meanwhile), so keys typed for the chat cannot approve by accident.
|
|
174
|
+
- Anything other than an explicit approval rejects: a dialog closed another way, an approver
|
|
175
|
+
with no running app, or a cancelled wait (the dialog is taken down so it cannot approve later).
|
|
176
|
+
- Rejecting does not cancel the turn: the model reads the refusal and answers.
|
|
177
|
+
|
|
178
|
+
The dialog is translucent: the chat stays visible behind it. Behind it the tool card reads
|
|
179
|
+
`○ set_light(…)` + `⎿ ◆ waiting for your approval`, the composer and the header `τ` turn Flare
|
|
180
|
+
and the hint bar says what to press. Nothing in this package approves on its own.
|
|
181
|
+
|
|
182
|
+
## Sessions and events
|
|
183
|
+
|
|
184
|
+
`/sessions` lists the ten most recent earlier sessions with messages (numbered for `/resume n`,
|
|
185
|
+
same order as `tau chat`); `/resume` alone resumes the most recent one. Resuming rebuilds the
|
|
186
|
+
agent on the stored session (the previous one is closed), renders the history from the stored
|
|
187
|
+
messages (user rows, markdown replies, tool cards with the recorded duration, approval and
|
|
188
|
+
result), replays the stored agent events into the events pane before live ones, and keeps the
|
|
189
|
+
current agent when the rebuild fails. The last 40 turns stay live; older ones fold into one
|
|
190
|
+
line and PageUp at the top pages them in. One `EventBus` serves the app for its lifetime; events
|
|
191
|
+
stamped with another session id are ignored.
|
|
192
|
+
|
|
193
|
+
`/compact [focus]` asks the agent's own model for a continuation summary, opens a new session
|
|
194
|
+
whose history starts with that summary, links the two sessions in their metadata
|
|
195
|
+
(`compacted_from` / `compacted_to`) and keeps the old one untouched; the transcript is reset
|
|
196
|
+
like `/new`, then shows the summary as an info card and the notice `Compacted N messages into a
|
|
197
|
+
summary. New session <id>; the previous session <old id> is kept.`, the header shows the new id
|
|
198
|
+
and the events pane starts empty. The summary is written in a worker, so the app stays
|
|
199
|
+
responsive: the chrome is busy (`✻ Summarizing…`, `esc cancel` in the hint bar), read-only
|
|
200
|
+
commands such as `/status` still answer, turns and session switches are refused, and Esc
|
|
201
|
+
cancels the summarizer (`Compaction cancelled; the session continues.`). A failure (nothing to
|
|
202
|
+
summarize, a model error, missing credentials) is a notice and the current session goes on.
|
|
203
|
+
The details are in tau-core's README under "Compaction".
|
|
204
|
+
|
|
205
|
+
`/model fast` rebuilds the agent on the same session with that role (the transcript stays,
|
|
206
|
+
the header and the welcome card follow) and remembers the role in the session's metadata, so
|
|
207
|
+
`/resume` and `tau tui --resume` come back on it; `build_app(role=...)` pins a role for the
|
|
208
|
+
whole run instead. An unknown role, missing credentials or an unavailable provider leave the
|
|
209
|
+
current agent working and say why.
|
|
210
|
+
|
|
211
|
+
## Knobs
|
|
212
|
+
|
|
213
|
+
| Setting | Effect |
|
|
214
|
+
|---|---|
|
|
215
|
+
| `TAU_TUI_ASCII=1` | ASCII vocabulary (`> t * o L * + x ! - >`), `ascii` box borders on every screen (the screens carry an `-ascii` class that `theme.ASCII_CSS` keys on), an Ember-only mark |
|
|
216
|
+
| `TAU_TUI_PLAIN_STREAM=1` | stream every reply as plain text and parse the markdown once per segment |
|
|
217
|
+
| `TEXTUAL_ANIMATIONS=none` | no header motion at all (also the default under `motion=False`) |
|
|
218
|
+
| `TEXTUAL_COLOR_SYSTEM` / `--ansi` | the `tau-ansi` theme for 16-colour terminals |
|
|
219
|
+
|
|
220
|
+
Live markdown streaming needs ≥ 60 cols; two consecutive slow appends (a long open code fence)
|
|
221
|
+
switch the segment to plain text until it ends. Replies are coalesced to at most one update every
|
|
222
|
+
80 ms.
|
|
223
|
+
|
|
224
|
+
## Logs
|
|
225
|
+
|
|
226
|
+
While the interface runs the terminal belongs to Textual: details go to
|
|
227
|
+
`<TAU_DATA_DIR>/logs/tau.log` (for example `~/.local/share/tau/logs/tau.log`), the stderr
|
|
228
|
+
handler of the `tau` command is detached for the duration, and the chat shows a short notice
|
|
229
|
+
instead of a traceback.
|
|
230
|
+
|
|
231
|
+
## Python API
|
|
232
|
+
|
|
233
|
+
```python
|
|
234
|
+
from tau_tui import TauApp, build_app, resolve_resume, SessionNotFound
|
|
235
|
+
from tau_tui import TuiApprover, ApprovalScreen, Transcript, EventLog, rasterise
|
|
236
|
+
|
|
237
|
+
app = build_app(config, session_store=store, session_id=None) # builds the agent (channel "tui")
|
|
238
|
+
app.run()
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
`build_app` creates one `EventBus` and one `TuiApprover`, builds the first agent with
|
|
242
|
+
`tau_core.build_agent(..., channel="tui")` and gives `TauApp` a factory
|
|
243
|
+
`agent_factory(session_id, *, role=None, seed_messages=None)` that builds every later agent
|
|
244
|
+
(`/new`, `/resume`, `/compact`, `/model`) on the same bus, store and approver. `role=None`
|
|
245
|
+
means `brain`, or the role a resumed `session_id` remembers from `/model`; an explicit `role`
|
|
246
|
+
is pinned for the run. Tests pass `model=FakeModel([...])`, `tools=[...]`, a fixed `clock`,
|
|
247
|
+
`tz=UTC`, `motion=False` and `show_durations=False`.
|
|
248
|
+
`resolve_resume(store, resume, language=...)` turns `--resume [ID]` into a session id and an
|
|
249
|
+
optional notice (`SessionNotFound` for an unknown id). `rasterise(cols)` is the τ mark as
|
|
250
|
+
half-block cells (`docs/brand/README.md`, "Terminal").
|
|
251
|
+
|
|
252
|
+
## Development
|
|
253
|
+
|
|
254
|
+
```sh
|
|
255
|
+
uv run pytest packages/tau-tui -q # pilot + snapshot tests, offline
|
|
256
|
+
uv run pytest packages/tau-tui --snapshot-update # after an intended visual change
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Snapshots live under `tests/__snapshots__/test_snapshots/` as `.raw` files: pytest-textual-snapshot
|
|
260
|
+
1.0 with syrupy 6 writes SVG under that extension, and the extension is pinned in
|
|
261
|
+
`test_snapshots.py` (exactly one file per snapshot test, no glob). There are twelve: welcome at
|
|
262
|
+
80x24, 50x24 and in Turkish, chat (a markdown list, a one-line fence and a rejected tier-2 card),
|
|
263
|
+
busy, approval, menu (all twelve rows) and picker at 80x24, wide 100x30 (busy chrome), ultra
|
|
264
|
+
160x45 (a fence and the last turn's cards expanded), the events view at 50x24 and short 80x18. Every snapshot uses the fixed session id
|
|
265
|
+
`20260929-120000-abcd` (checked in every file), a fixed clock, UTC event times, no motion and no
|
|
266
|
+
durations, and is taken after the streams stopped; review a regenerated file before committing it
|
|
267
|
+
(it must never contain a local path, which the test also checks). The pilot tests read the widgets
|
|
268
|
+
through text oracles (`Transcript.text`, `EventLog.text`, `HintBar.text`, `HeaderBar.text`,
|
|
269
|
+
`ToolCard.text`) so behaviour changes do not churn the snapshots. The mark fixtures under
|
|
270
|
+
`tests/fixtures/` are the text previews of `rasterise(24)` and `rasterise(16)`; regenerate them
|
|
271
|
+
together with the pinned SHA-256 in `test_brand.py` when the geometry in `docs/brand/render.py`
|
|
272
|
+
changes.
|
|
273
|
+
|
|
274
|
+
License: MIT.
|
tau_tui-0.2.0/README.md
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
# tau-tui
|
|
2
|
+
|
|
3
|
+
The [Textual](https://textual.textualize.io/) terminal interface for tau: replies stream in as
|
|
4
|
+
markdown, tool calls are cards with their tier, approval and result, agent events show in a live
|
|
5
|
+
pane, tier-2 calls ask in a modal dialog, and the brand's τ mark opens every new session. One row
|
|
6
|
+
of chrome at the top, at most four at the bottom, so 80x24 over mosh on a phone stays usable.
|
|
7
|
+
|
|
8
|
+
It is its own distribution (`tau-tui`, import `tau_tui`), uses only the public API of `tau-core`
|
|
9
|
+
and plugs into the `tau` command through the `tau.commands` entry point (`tui`):
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
pip install tau-tui # or, in this repo: uv sync --all-packages --group dev
|
|
13
|
+
tau tui # new session
|
|
14
|
+
tau tui --resume # continue the most recent earlier session
|
|
15
|
+
tau tui --resume ID # continue a given session
|
|
16
|
+
tau --home /path/to/tau --lang tr tui --profile travel
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Configuration comes from `tau.toml` and `.env` like every other channel (`TAU_HOME`,
|
|
20
|
+
`TAU_DATA_DIR`, `TAU_PROFILE`, `TAU_LANG`). Missing credentials, a broken config or an unknown
|
|
21
|
+
session end the command with a plain English message and exit code 2 before the interface opens.
|
|
22
|
+
The chrome (hints, labels, notices, the approval dialog) follows `[ui] language` (`en` or `tr`);
|
|
23
|
+
`/lang` switches it for one run.
|
|
24
|
+
|
|
25
|
+
## Layout
|
|
26
|
+
|
|
27
|
+
Four bands by width (`-phone` < 60 cols, `-narrow` 60–99, `-wide` 100–139, `-ultra` ≥ 140) and
|
|
28
|
+
three by height (`-short` < 20 rows, `-mid`, `-tall` ≥ 30). The header is one row, the composer a
|
|
29
|
+
rounded box of one to five text rows, the hint bar one row.
|
|
30
|
+
|
|
31
|
+
80x24 (`-narrow`), idle after a two-tool turn:
|
|
32
|
+
|
|
33
|
+
```text
|
|
34
|
+
τ tau home · 20260929-120000-abcd brain · en
|
|
35
|
+
› Salon lambası açık mı? Açıksa kapat.
|
|
36
|
+
● get_light_state(room="salon") tier 0 · 38ms
|
|
37
|
+
⎿ {"on": true, "brightness": 80}
|
|
38
|
+
● set_light(room="salon", on=false) tier 2 · approved · 412ms
|
|
39
|
+
⎿ Light turned off
|
|
40
|
+
τ Salon lambası kapatıldı.
|
|
41
|
+
• Önceki durum: açık, %80 parlaklık
|
|
42
|
+
✓ Worked for 4s · 2 tool calls · ctrl+o expand
|
|
43
|
+
╭──────────────────────────────────────────────────────────────────────────────╮
|
|
44
|
+
│ ❯ Type a message… │
|
|
45
|
+
╰──────────────────────────────────────────────────────────────────────────────╯
|
|
46
|
+
? shortcuts · / commands · ctrl+j newline ctrl+q quit
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
100x30 (`-wide`): the events pane docks on the right (34 cols; 40 at `-ultra`), `ctrl+e` hides
|
|
50
|
+
it. 160x45 (`-ultra`): a read-only session sidebar (28 cols) on the left; clicking a row resumes.
|
|
51
|
+
|
|
52
|
+
```text
|
|
53
|
+
τ tau home · 20260929-120000-abcd brain · 7 tools · en
|
|
54
|
+
› Salon lambası açık mı? Açıksa kapat. │ Events · 5
|
|
55
|
+
● get_light_state(room="salon") tier 0 · 38ms │ 12:00:01 ● get_light_state
|
|
56
|
+
⎿ {"on": true, "brightness": 80} │ tier 0 · ok · 38ms
|
|
57
|
+
τ Salon lambası kapatıldı. │ 12:00:02 ◆ approval set_light
|
|
58
|
+
│ tier 2 → approved (tui)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
80x30 (`-narrow -tall`): an 8-row events strip under the transcript. Below 100 cols `ctrl+e` /
|
|
62
|
+
F4 swaps the transcript for the events log in the same body; the composer stays live and Esc
|
|
63
|
+
returns to the chat.
|
|
64
|
+
|
|
65
|
+
50x24 (`-phone`, Termius over mosh): the header drops the session id (`/session` shows it),
|
|
66
|
+
tool badges move to the result line, the composer has no border, hints say `^j`, the welcome
|
|
67
|
+
card shows the 16-column mark, replies stream as plain text and settle into markdown when the
|
|
68
|
+
segment ends, and the approval dialog is full width with stacked buttons.
|
|
69
|
+
|
|
70
|
+
```text
|
|
71
|
+
τ tau · home en
|
|
72
|
+
› Salon lambası açık mı? Açıksa kapat.
|
|
73
|
+
○ set_light(room="salon", on=false)
|
|
74
|
+
⎿ tier 2 · running
|
|
75
|
+
✻ Running set_light…
|
|
76
|
+
❯
|
|
77
|
+
esc cancel · ^j newline busy
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
`-short` (< 20 rows): no hint row, the welcome mark is hidden, the composer grows to two rows.
|
|
81
|
+
|
|
82
|
+
## Vocabulary
|
|
83
|
+
|
|
84
|
+
Every glyph is one cell wide and none is an emoji-presentation code point, so phone terminals
|
|
85
|
+
draw them at the right width: `›` you · `τ` tau · `●` finished tool call · `○` running or waiting
|
|
86
|
+
for approval · `⎿` result line · `✻` busy verb / reasoning · `✓ ◆ ✗` turn footer (finished /
|
|
87
|
+
limit, cancelled, interrupted / error) · `◆` needs you · `▸` notice · `❯` prompt.
|
|
88
|
+
|
|
89
|
+
State colours (deliberate; no green and no red in the palette, shapes double the colours):
|
|
90
|
+
**ok / success = Flare**, **error = Signal**, **rejected = Ember**, **running / pending = Core at
|
|
91
|
+
55 %**, **attention = Flare**, notice = Ember. Rich renderables (the events pane) cannot take a TCSS
|
|
92
|
+
alpha, so they use the literal blends `theme.TEXT_MUTED` (Core over Void at 60 %, the live clock and
|
|
93
|
+
the running dot) and `theme.TEXT_DISABLED` (38 %, replayed clocks). The header `τ` breathes while the welcome card is
|
|
94
|
+
on screen, runs the comet while a turn is busy and is static Ember otherwise; nothing else in
|
|
95
|
+
the UI moves. The composer border is Ember, Signal while busy and Flare while an approval waits.
|
|
96
|
+
|
|
97
|
+
## Keys and commands
|
|
98
|
+
|
|
99
|
+
| Key | Effect |
|
|
100
|
+
|---|---|
|
|
101
|
+
| Enter | send (or accept the highlighted menu row); while tau answers the text stays and a toast says so |
|
|
102
|
+
| ctrl+j | newline (shift+enter too where the terminal delivers it) |
|
|
103
|
+
| Esc | close the menu · cancel the running turn · leave the events view · scroll back to the bottom |
|
|
104
|
+
| ctrl+e / F4 | events: toggle the column (≥ 100 cols) or the view (< 100 cols) |
|
|
105
|
+
| ctrl+r / F2 | session picker (Enter resumes, `n` new, Esc closes) |
|
|
106
|
+
| ctrl+n | new session |
|
|
107
|
+
| ctrl+t / F3 | `/tools` |
|
|
108
|
+
| ctrl+o / F6 | expand or collapse the cards of the last turn: the arguments as JSON and the full result (a click toggles any card) |
|
|
109
|
+
| PageUp / PageDown, F7 / F8 | scroll the transcript; PageUp at the top of a folded history loads 20 older turns |
|
|
110
|
+
| F9 | back to the bottom |
|
|
111
|
+
| ↑ / ↓ | recall sent prompts (single-line composer); navigate the menu while it is open |
|
|
112
|
+
| `?` or F1 | help (only in an empty composer; `?x` is sent as a message) |
|
|
113
|
+
| ctrl+q | quit (ctrl+c copies a selection in the composer; without one it only says how to quit) |
|
|
114
|
+
|
|
115
|
+
Typing `/` into an empty composer opens a fuzzy menu of the slash commands (`↑↓` pick, Tab
|
|
116
|
+
complete, Enter run, Esc close). An exact name ranks first, and a command typed in full runs as
|
|
117
|
+
typed even when the menu has not caught up with a burst of keys (a paste, mosh), so `/session`
|
|
118
|
+
never runs `/sessions`. The commands are the shared spec of `tau_core.commands`, with
|
|
119
|
+
the same wording as `tau chat`:
|
|
120
|
+
|
|
121
|
+
| command | effect |
|
|
122
|
+
|---|---|
|
|
123
|
+
| `/help`, `/tools`, `/sessions`, `/session`, `/lang en\|tr` | as in `tau chat` |
|
|
124
|
+
| `/resume [n\|id]`, `/new` | switch sessions (see below) |
|
|
125
|
+
| `/compact [focus]` | summarize this session with its own model and continue in a new session seeded with the summary; the old session is kept |
|
|
126
|
+
| `/clear` | empty the transcript (turns, welcome card, info cards, the fold); the session and the agent go on, `/resume` or `--resume` show the history again |
|
|
127
|
+
| `/status` | an info card: profile, session, model, context, tools, language, versions, hub |
|
|
128
|
+
| `/model [role]` | list the roles of `tau.toml`, or switch this session to one; the header follows |
|
|
129
|
+
| `/quit` (alias `/exit`) | leave |
|
|
130
|
+
|
|
131
|
+
The Turkish aliases (`/devam`, `/çık`, `/özet`, `/temizle`, `/durum`, `/rol`, …) keep working
|
|
132
|
+
but are never listed; the menu shows the twelve canonical names and every row is on screen at
|
|
133
|
+
80x24 (in the `-short` band it scrolls). `/new`, `/resume`, `/compact`, `/clear` and
|
|
134
|
+
`/model <role>` wait for the current turn (a toast says so); `/status` and `/model` without an
|
|
135
|
+
argument run any time.
|
|
136
|
+
|
|
137
|
+
## Approvals
|
|
138
|
+
|
|
139
|
+
`TuiApprover` (`name = "tui"`) is the channel's `Approver`. A tier-2 call opens `ApprovalScreen`:
|
|
140
|
+
tool, effect, the arguments as pretty JSON and the buttons **Approve** / **Reject**.
|
|
141
|
+
|
|
142
|
+
- `y` or `e` approves, `n`, `h` or Esc rejects; focus starts on **Reject**, so Enter rejects, and
|
|
143
|
+
Tab only moves between the two buttons (PageUp/PageDown scroll the arguments). The keys work in
|
|
144
|
+
both languages whatever the UI setting.
|
|
145
|
+
- `y`/`e` and **Approve** only work 0.5 s after the dialog opens (the hint reads `(arming…)`
|
|
146
|
+
meanwhile), so keys typed for the chat cannot approve by accident.
|
|
147
|
+
- Anything other than an explicit approval rejects: a dialog closed another way, an approver
|
|
148
|
+
with no running app, or a cancelled wait (the dialog is taken down so it cannot approve later).
|
|
149
|
+
- Rejecting does not cancel the turn: the model reads the refusal and answers.
|
|
150
|
+
|
|
151
|
+
The dialog is translucent: the chat stays visible behind it. Behind it the tool card reads
|
|
152
|
+
`○ set_light(…)` + `⎿ ◆ waiting for your approval`, the composer and the header `τ` turn Flare
|
|
153
|
+
and the hint bar says what to press. Nothing in this package approves on its own.
|
|
154
|
+
|
|
155
|
+
## Sessions and events
|
|
156
|
+
|
|
157
|
+
`/sessions` lists the ten most recent earlier sessions with messages (numbered for `/resume n`,
|
|
158
|
+
same order as `tau chat`); `/resume` alone resumes the most recent one. Resuming rebuilds the
|
|
159
|
+
agent on the stored session (the previous one is closed), renders the history from the stored
|
|
160
|
+
messages (user rows, markdown replies, tool cards with the recorded duration, approval and
|
|
161
|
+
result), replays the stored agent events into the events pane before live ones, and keeps the
|
|
162
|
+
current agent when the rebuild fails. The last 40 turns stay live; older ones fold into one
|
|
163
|
+
line and PageUp at the top pages them in. One `EventBus` serves the app for its lifetime; events
|
|
164
|
+
stamped with another session id are ignored.
|
|
165
|
+
|
|
166
|
+
`/compact [focus]` asks the agent's own model for a continuation summary, opens a new session
|
|
167
|
+
whose history starts with that summary, links the two sessions in their metadata
|
|
168
|
+
(`compacted_from` / `compacted_to`) and keeps the old one untouched; the transcript is reset
|
|
169
|
+
like `/new`, then shows the summary as an info card and the notice `Compacted N messages into a
|
|
170
|
+
summary. New session <id>; the previous session <old id> is kept.`, the header shows the new id
|
|
171
|
+
and the events pane starts empty. The summary is written in a worker, so the app stays
|
|
172
|
+
responsive: the chrome is busy (`✻ Summarizing…`, `esc cancel` in the hint bar), read-only
|
|
173
|
+
commands such as `/status` still answer, turns and session switches are refused, and Esc
|
|
174
|
+
cancels the summarizer (`Compaction cancelled; the session continues.`). A failure (nothing to
|
|
175
|
+
summarize, a model error, missing credentials) is a notice and the current session goes on.
|
|
176
|
+
The details are in tau-core's README under "Compaction".
|
|
177
|
+
|
|
178
|
+
`/model fast` rebuilds the agent on the same session with that role (the transcript stays,
|
|
179
|
+
the header and the welcome card follow) and remembers the role in the session's metadata, so
|
|
180
|
+
`/resume` and `tau tui --resume` come back on it; `build_app(role=...)` pins a role for the
|
|
181
|
+
whole run instead. An unknown role, missing credentials or an unavailable provider leave the
|
|
182
|
+
current agent working and say why.
|
|
183
|
+
|
|
184
|
+
## Knobs
|
|
185
|
+
|
|
186
|
+
| Setting | Effect |
|
|
187
|
+
|---|---|
|
|
188
|
+
| `TAU_TUI_ASCII=1` | ASCII vocabulary (`> t * o L * + x ! - >`), `ascii` box borders on every screen (the screens carry an `-ascii` class that `theme.ASCII_CSS` keys on), an Ember-only mark |
|
|
189
|
+
| `TAU_TUI_PLAIN_STREAM=1` | stream every reply as plain text and parse the markdown once per segment |
|
|
190
|
+
| `TEXTUAL_ANIMATIONS=none` | no header motion at all (also the default under `motion=False`) |
|
|
191
|
+
| `TEXTUAL_COLOR_SYSTEM` / `--ansi` | the `tau-ansi` theme for 16-colour terminals |
|
|
192
|
+
|
|
193
|
+
Live markdown streaming needs ≥ 60 cols; two consecutive slow appends (a long open code fence)
|
|
194
|
+
switch the segment to plain text until it ends. Replies are coalesced to at most one update every
|
|
195
|
+
80 ms.
|
|
196
|
+
|
|
197
|
+
## Logs
|
|
198
|
+
|
|
199
|
+
While the interface runs the terminal belongs to Textual: details go to
|
|
200
|
+
`<TAU_DATA_DIR>/logs/tau.log` (for example `~/.local/share/tau/logs/tau.log`), the stderr
|
|
201
|
+
handler of the `tau` command is detached for the duration, and the chat shows a short notice
|
|
202
|
+
instead of a traceback.
|
|
203
|
+
|
|
204
|
+
## Python API
|
|
205
|
+
|
|
206
|
+
```python
|
|
207
|
+
from tau_tui import TauApp, build_app, resolve_resume, SessionNotFound
|
|
208
|
+
from tau_tui import TuiApprover, ApprovalScreen, Transcript, EventLog, rasterise
|
|
209
|
+
|
|
210
|
+
app = build_app(config, session_store=store, session_id=None) # builds the agent (channel "tui")
|
|
211
|
+
app.run()
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
`build_app` creates one `EventBus` and one `TuiApprover`, builds the first agent with
|
|
215
|
+
`tau_core.build_agent(..., channel="tui")` and gives `TauApp` a factory
|
|
216
|
+
`agent_factory(session_id, *, role=None, seed_messages=None)` that builds every later agent
|
|
217
|
+
(`/new`, `/resume`, `/compact`, `/model`) on the same bus, store and approver. `role=None`
|
|
218
|
+
means `brain`, or the role a resumed `session_id` remembers from `/model`; an explicit `role`
|
|
219
|
+
is pinned for the run. Tests pass `model=FakeModel([...])`, `tools=[...]`, a fixed `clock`,
|
|
220
|
+
`tz=UTC`, `motion=False` and `show_durations=False`.
|
|
221
|
+
`resolve_resume(store, resume, language=...)` turns `--resume [ID]` into a session id and an
|
|
222
|
+
optional notice (`SessionNotFound` for an unknown id). `rasterise(cols)` is the τ mark as
|
|
223
|
+
half-block cells (`docs/brand/README.md`, "Terminal").
|
|
224
|
+
|
|
225
|
+
## Development
|
|
226
|
+
|
|
227
|
+
```sh
|
|
228
|
+
uv run pytest packages/tau-tui -q # pilot + snapshot tests, offline
|
|
229
|
+
uv run pytest packages/tau-tui --snapshot-update # after an intended visual change
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Snapshots live under `tests/__snapshots__/test_snapshots/` as `.raw` files: pytest-textual-snapshot
|
|
233
|
+
1.0 with syrupy 6 writes SVG under that extension, and the extension is pinned in
|
|
234
|
+
`test_snapshots.py` (exactly one file per snapshot test, no glob). There are twelve: welcome at
|
|
235
|
+
80x24, 50x24 and in Turkish, chat (a markdown list, a one-line fence and a rejected tier-2 card),
|
|
236
|
+
busy, approval, menu (all twelve rows) and picker at 80x24, wide 100x30 (busy chrome), ultra
|
|
237
|
+
160x45 (a fence and the last turn's cards expanded), the events view at 50x24 and short 80x18. Every snapshot uses the fixed session id
|
|
238
|
+
`20260929-120000-abcd` (checked in every file), a fixed clock, UTC event times, no motion and no
|
|
239
|
+
durations, and is taken after the streams stopped; review a regenerated file before committing it
|
|
240
|
+
(it must never contain a local path, which the test also checks). The pilot tests read the widgets
|
|
241
|
+
through text oracles (`Transcript.text`, `EventLog.text`, `HintBar.text`, `HeaderBar.text`,
|
|
242
|
+
`ToolCard.text`) so behaviour changes do not churn the snapshots. The mark fixtures under
|
|
243
|
+
`tests/fixtures/` are the text previews of `rasterise(24)` and `rasterise(16)`; regenerate them
|
|
244
|
+
together with the pinned SHA-256 in `test_brand.py` when the geometry in `docs/brand/render.py`
|
|
245
|
+
changes.
|
|
246
|
+
|
|
247
|
+
License: MIT.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "tau-tui"
|
|
3
|
+
version = "0.2.0"
|
|
4
|
+
description = "Textual terminal UI for tau: streamed markdown replies, tool cards, live events and in-TUI approvals."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
requires-python = ">=3.12"
|
|
8
|
+
dependencies = ["click>=8.1", "rich>=13", "tau-core>=0.1.0", "textual>=8.2,<9"]
|
|
9
|
+
keywords = ["tau", "agent", "assistant", "tui", "textual", "terminal", "cli"]
|
|
10
|
+
classifiers = [
|
|
11
|
+
"Development Status :: 3 - Alpha",
|
|
12
|
+
"Environment :: Console",
|
|
13
|
+
"Intended Audience :: End Users/Desktop",
|
|
14
|
+
"License :: OSI Approved :: MIT License",
|
|
15
|
+
"Operating System :: MacOS",
|
|
16
|
+
"Operating System :: POSIX :: Linux",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Programming Language :: Python :: 3.12",
|
|
19
|
+
"Topic :: Home Automation",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
[project.urls]
|
|
23
|
+
Homepage = "https://github.com/fport/tau"
|
|
24
|
+
Documentation = "https://docs.tau.getporti.com"
|
|
25
|
+
Issues = "https://github.com/fport/tau/issues"
|
|
26
|
+
Changelog = "https://github.com/fport/tau/releases"
|
|
27
|
+
|
|
28
|
+
[project.entry-points."tau.commands"]
|
|
29
|
+
tui = "tau_tui.cli:tui"
|
|
30
|
+
|
|
31
|
+
[build-system]
|
|
32
|
+
requires = ["hatchling"]
|
|
33
|
+
build-backend = "hatchling.build"
|
|
34
|
+
|
|
35
|
+
[tool.hatch.build.targets.wheel]
|
|
36
|
+
packages = ["src/tau_tui"]
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""tau-tui: the Textual terminal interface for tau.
|
|
2
|
+
|
|
3
|
+
Public API::
|
|
4
|
+
|
|
5
|
+
TauApp, build_app, resolve_resume, SessionNotFound
|
|
6
|
+
TuiApprover, ApprovalScreen
|
|
7
|
+
Transcript, EventLog, rasterise
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
11
|
+
|
|
12
|
+
import tau_tui.strings # noqa: F401 (registers the tui.* message keys)
|
|
13
|
+
from tau_tui.app import SessionNotFound, TauApp, build_app, resolve_resume
|
|
14
|
+
from tau_tui.approval import ApprovalScreen, TuiApprover
|
|
15
|
+
from tau_tui.brand import rasterise
|
|
16
|
+
from tau_tui.events import EventLog
|
|
17
|
+
from tau_tui.transcript import Transcript
|
|
18
|
+
|
|
19
|
+
try:
|
|
20
|
+
__version__ = version("tau-tui")
|
|
21
|
+
except PackageNotFoundError:
|
|
22
|
+
__version__ = "0.0.0"
|
|
23
|
+
|
|
24
|
+
__all__ = [
|
|
25
|
+
"ApprovalScreen",
|
|
26
|
+
"EventLog",
|
|
27
|
+
"SessionNotFound",
|
|
28
|
+
"TauApp",
|
|
29
|
+
"Transcript",
|
|
30
|
+
"TuiApprover",
|
|
31
|
+
"__version__",
|
|
32
|
+
"build_app",
|
|
33
|
+
"rasterise",
|
|
34
|
+
"resolve_resume",
|
|
35
|
+
]
|