deepy-cli 0.2.25__tar.gz → 0.2.27__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.
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/PKG-INFO +26 -13
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/README.md +24 -11
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/pyproject.toml +2 -2
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/__init__.py +1 -1
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/audit.py +2 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/cli.py +53 -7
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/config/__init__.py +24 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/config/settings.py +134 -1
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/data/tools/test_shell.md +5 -3
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/llm/compaction.py +1 -1
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/llm/context.py +26 -1
- deepy_cli-0.2.27/src/deepy/llm/multimodal.py +279 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/llm/provider.py +18 -1
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/llm/replay.py +11 -1
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/llm/runner.py +64 -5
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/prompts/system.py +3 -4
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/sessions/session.py +21 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/sessions/store_helpers.py +3 -10
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/status.py +4 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/tools/agents.py +38 -1
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/tools/builtin.py +269 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/tools/test_shell.py +21 -6
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/tui/app.py +1239 -222
- deepy_cli-0.2.27/src/deepy/tui/commands.py +86 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/tui/compat.py +1 -1
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/tui/diff.py +53 -9
- deepy_cli-0.2.27/src/deepy/tui/interaction_surfaces.py +121 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/tui/runner.py +6 -4
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/tui/screens.py +201 -110
- deepy_cli-0.2.27/src/deepy/tui/theme.py +50 -0
- deepy_cli-0.2.27/src/deepy/tui/transcript.py +43 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/tui/widgets.py +777 -140
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/ui/audit_approval_panel.py +19 -65
- deepy_cli-0.2.27/src/deepy/ui/image_input.py +527 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/ui/local_command.py +1 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/ui/markdown.py +15 -2
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/ui/message_view.py +99 -29
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/ui/prompt_input.py +102 -3
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/ui/slash_commands.py +78 -28
- deepy_cli-0.2.27/src/deepy/ui/syntax.py +88 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/ui/terminal.py +220 -18
- deepy_cli-0.2.25/src/deepy/tui/commands.py +0 -105
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/__main__.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/background_tasks.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/data/__init__.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/data/skills/skill-creator/SKILL.md +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/data/skills/skill-installer/SKILL.md +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/data/tools/AskUserQuestion.md +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/data/tools/Read.md +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/data/tools/Search.md +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/data/tools/Update.md +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/data/tools/WebFetch.md +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/data/tools/WebSearch.md +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/data/tools/Write.md +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/data/tools/__init__.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/data/tools/shell.md +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/data/tools/task_list.md +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/data/tools/task_output.md +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/data/tools/task_stop.md +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/data/tools/todo_write.md +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/errors.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/input_suggestions.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/llm/__init__.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/llm/agent.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/llm/cache_context.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/llm/events.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/llm/model_capabilities.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/llm/thinking.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/mcp.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/prompts/__init__.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/prompts/compact.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/prompts/init_agents.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/prompts/rules.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/prompts/runtime_context.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/prompts/tool_docs.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/session_cost.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/sessions/__init__.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/sessions/index.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/sessions/manager.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/skill_market.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/skills.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/subagents.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/todos.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/tools/__init__.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/tools/file_state.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/tools/result.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/tools/search.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/tools/shell_output.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/tools/shell_utils.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/tui/__init__.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/tui/state.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/types/__init__.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/types/sdk.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/types/tool_payloads.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/ui/__init__.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/ui/app.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/ui/ask_user_question.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/ui/audit_approval_picker.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/ui/exit_summary.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/ui/file_mentions.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/ui/loading_text.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/ui/model_picker.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/ui/prompt_buffer.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/ui/session_list.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/ui/session_picker.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/ui/skill_picker.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/ui/status_footer.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/ui/styles.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/ui/theme_picker.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/ui/thinking_state.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/ui/welcome.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/update_check.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/usage.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/utils/__init__.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/utils/debug_logger.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/utils/error_logger.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/src/deepy/utils/json.py +0 -0
- {deepy_cli-0.2.25 → deepy_cli-0.2.27}/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.
|
|
3
|
+
Version: 0.2.27
|
|
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
|
|
@@ -21,7 +21,7 @@ Requires-Dist: prompt-toolkit>=3.0,<4
|
|
|
21
21
|
Requires-Dist: regex>=2026.5.9
|
|
22
22
|
Requires-Dist: pyyaml>=6.0,<7
|
|
23
23
|
Requires-Dist: rich>=14.2,<15
|
|
24
|
-
Requires-Dist: textual>=8.2,<9
|
|
24
|
+
Requires-Dist: textual>=8.2.7,<9
|
|
25
25
|
Requires-Dist: tiktoken>=0.9,<1
|
|
26
26
|
Requires-Dist: tomli-w>=1
|
|
27
27
|
Requires-Python: >=3.12
|
|
@@ -162,24 +162,35 @@ for direct local commands.
|
|
|
162
162
|
|
|
163
163
|

|
|
164
164
|
|
|
165
|
-
##
|
|
165
|
+
## Classic UI And Modern UI
|
|
166
166
|
|
|
167
|
-
|
|
168
|
-
|
|
167
|
+
Deepy has two peer terminal interfaces. Classic UI is the Rich/prompt-toolkit
|
|
168
|
+
terminal UI, and Modern UI is the Textual terminal UI. The default `deepy`
|
|
169
|
+
command starts the UI saved in `ui.interface`; missing config defaults to
|
|
170
|
+
Classic UI with the dark theme. The compatibility command below still starts
|
|
171
|
+
Modern UI directly:
|
|
169
172
|
|
|
170
173
|
```bash
|
|
171
174
|
deepy tui
|
|
172
175
|
```
|
|
173
176
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
177
|
+
Modern UI has a compact scrollable transcript, lightweight status line,
|
|
178
|
+
Textual-native composer, live assistant/tool activity states, compact tool
|
|
179
|
+
summary rows, slash-command and `@file` suggestions, inline audit/model/theme
|
|
180
|
+
choices, status/help screens, and a Deepy-owned diff view. Use `/ui` in either
|
|
181
|
+
interface to change the default UI for the next `deepy` startup.
|
|
179
182
|
|
|
180
183
|
`/status` shows session/project usage, context-window pressure, and DeepSeek
|
|
181
|
-
balance in one panel. Exiting
|
|
182
|
-
as
|
|
184
|
+
balance in one panel. Exiting Modern UI prints the same compact session summary
|
|
185
|
+
as Classic UI. Prompt text, image attachments, generated input
|
|
186
|
+
suggestions, slash suggestions, and file suggestions are separate composer
|
|
187
|
+
states, so UI-only attachment labels are not written into the editable prompt
|
|
188
|
+
buffer. Attached images appear in an attachment row with prompt-local keyboard
|
|
189
|
+
shortcuts: press Down to enter attachment selection, Left/Right to choose,
|
|
190
|
+
Backspace to remove, and Up to return to normal input. The shared `dark` /
|
|
191
|
+
`light` setting maps to curated
|
|
192
|
+
Textual themes in Modern UI, with `dark` defaulting to `tokyo-night`; Modern UI can
|
|
193
|
+
also save a Textual-only `ui.textual_theme` override from `/theme`.
|
|
183
194
|
|
|
184
195
|
See [docs/deepy-ui-and-tui.md](docs/deepy-ui-and-tui.md) for the full feature
|
|
185
196
|
comparison and current limitations.
|
|
@@ -263,7 +274,7 @@ MCP inheritance, and troubleshooting.
|
|
|
263
274
|
| Tutorial videos | [docs/tutorial-videos.md](docs/tutorial-videos.md) | [docs/tutorial-videos.zh-CN.md](docs/tutorial-videos.zh-CN.md) |
|
|
264
275
|
| MCP setup and troubleshooting | [docs/mcp.md](docs/mcp.md) | [docs/mcp.zh-CN.md](docs/mcp.zh-CN.md) |
|
|
265
276
|
| Subagents and custom subagents | [docs/subagents.md](docs/subagents.md) | [docs/subagents.zh-CN.md](docs/subagents.zh-CN.md) |
|
|
266
|
-
|
|
|
277
|
+
| Classic UI and Modern UI | [docs/deepy-ui-and-tui.md](docs/deepy-ui-and-tui.md) | [docs/deepy-ui-and-tui.zh-CN.md](docs/deepy-ui-and-tui.zh-CN.md) |
|
|
267
278
|
|
|
268
279
|
## Command Reference
|
|
269
280
|
|
|
@@ -301,6 +312,7 @@ Inside the interactive terminal:
|
|
|
301
312
|
/<name> [request] Invoke a Skill directly
|
|
302
313
|
/init Create or update project AGENTS.md
|
|
303
314
|
/theme Show or change terminal UI theme
|
|
315
|
+
/ui Show or change Classic/Modern UI
|
|
304
316
|
/ps Show managed background shell tasks
|
|
305
317
|
/stop Choose background shell tasks to stop
|
|
306
318
|
```
|
|
@@ -331,6 +343,7 @@ compact_preserve_recent_messages = 2
|
|
|
331
343
|
mode = "yolo" # normal, auto, or yolo
|
|
332
344
|
|
|
333
345
|
[ui]
|
|
346
|
+
interface = "classic" # classic or modern
|
|
334
347
|
theme = "dark" # dark or light
|
|
335
348
|
```
|
|
336
349
|
|
|
@@ -130,24 +130,35 @@ for direct local commands.
|
|
|
130
130
|
|
|
131
131
|

|
|
132
132
|
|
|
133
|
-
##
|
|
133
|
+
## Classic UI And Modern UI
|
|
134
134
|
|
|
135
|
-
|
|
136
|
-
|
|
135
|
+
Deepy has two peer terminal interfaces. Classic UI is the Rich/prompt-toolkit
|
|
136
|
+
terminal UI, and Modern UI is the Textual terminal UI. The default `deepy`
|
|
137
|
+
command starts the UI saved in `ui.interface`; missing config defaults to
|
|
138
|
+
Classic UI with the dark theme. The compatibility command below still starts
|
|
139
|
+
Modern UI directly:
|
|
137
140
|
|
|
138
141
|
```bash
|
|
139
142
|
deepy tui
|
|
140
143
|
```
|
|
141
144
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
145
|
+
Modern UI has a compact scrollable transcript, lightweight status line,
|
|
146
|
+
Textual-native composer, live assistant/tool activity states, compact tool
|
|
147
|
+
summary rows, slash-command and `@file` suggestions, inline audit/model/theme
|
|
148
|
+
choices, status/help screens, and a Deepy-owned diff view. Use `/ui` in either
|
|
149
|
+
interface to change the default UI for the next `deepy` startup.
|
|
147
150
|
|
|
148
151
|
`/status` shows session/project usage, context-window pressure, and DeepSeek
|
|
149
|
-
balance in one panel. Exiting
|
|
150
|
-
as
|
|
152
|
+
balance in one panel. Exiting Modern UI prints the same compact session summary
|
|
153
|
+
as Classic UI. Prompt text, image attachments, generated input
|
|
154
|
+
suggestions, slash suggestions, and file suggestions are separate composer
|
|
155
|
+
states, so UI-only attachment labels are not written into the editable prompt
|
|
156
|
+
buffer. Attached images appear in an attachment row with prompt-local keyboard
|
|
157
|
+
shortcuts: press Down to enter attachment selection, Left/Right to choose,
|
|
158
|
+
Backspace to remove, and Up to return to normal input. The shared `dark` /
|
|
159
|
+
`light` setting maps to curated
|
|
160
|
+
Textual themes in Modern UI, with `dark` defaulting to `tokyo-night`; Modern UI can
|
|
161
|
+
also save a Textual-only `ui.textual_theme` override from `/theme`.
|
|
151
162
|
|
|
152
163
|
See [docs/deepy-ui-and-tui.md](docs/deepy-ui-and-tui.md) for the full feature
|
|
153
164
|
comparison and current limitations.
|
|
@@ -231,7 +242,7 @@ MCP inheritance, and troubleshooting.
|
|
|
231
242
|
| Tutorial videos | [docs/tutorial-videos.md](docs/tutorial-videos.md) | [docs/tutorial-videos.zh-CN.md](docs/tutorial-videos.zh-CN.md) |
|
|
232
243
|
| MCP setup and troubleshooting | [docs/mcp.md](docs/mcp.md) | [docs/mcp.zh-CN.md](docs/mcp.zh-CN.md) |
|
|
233
244
|
| Subagents and custom subagents | [docs/subagents.md](docs/subagents.md) | [docs/subagents.zh-CN.md](docs/subagents.zh-CN.md) |
|
|
234
|
-
|
|
|
245
|
+
| Classic UI and Modern UI | [docs/deepy-ui-and-tui.md](docs/deepy-ui-and-tui.md) | [docs/deepy-ui-and-tui.zh-CN.md](docs/deepy-ui-and-tui.zh-CN.md) |
|
|
235
246
|
|
|
236
247
|
## Command Reference
|
|
237
248
|
|
|
@@ -269,6 +280,7 @@ Inside the interactive terminal:
|
|
|
269
280
|
/<name> [request] Invoke a Skill directly
|
|
270
281
|
/init Create or update project AGENTS.md
|
|
271
282
|
/theme Show or change terminal UI theme
|
|
283
|
+
/ui Show or change Classic/Modern UI
|
|
272
284
|
/ps Show managed background shell tasks
|
|
273
285
|
/stop Choose background shell tasks to stop
|
|
274
286
|
```
|
|
@@ -299,6 +311,7 @@ compact_preserve_recent_messages = 2
|
|
|
299
311
|
mode = "yolo" # normal, auto, or yolo
|
|
300
312
|
|
|
301
313
|
[ui]
|
|
314
|
+
interface = "classic" # classic or modern
|
|
302
315
|
theme = "dark" # dark or light
|
|
303
316
|
```
|
|
304
317
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "deepy-cli"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.27"
|
|
4
4
|
description = "Deepy - Vibe coding for DeepSeek models in your terminal"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [
|
|
@@ -27,7 +27,7 @@ dependencies = [
|
|
|
27
27
|
"regex>=2026.5.9",
|
|
28
28
|
"pyyaml>=6.0,<7",
|
|
29
29
|
"rich>=14.2,<15",
|
|
30
|
-
"textual>=8.2,<9",
|
|
30
|
+
"textual>=8.2.7,<9",
|
|
31
31
|
"tiktoken>=0.9,<1",
|
|
32
32
|
"tomli-w>=1",
|
|
33
33
|
]
|
|
@@ -136,9 +136,11 @@ class PendingApproval:
|
|
|
136
136
|
name: str
|
|
137
137
|
tool_name: str
|
|
138
138
|
arguments: str
|
|
139
|
+
call_id: str = ""
|
|
139
140
|
agent_name: str = ""
|
|
140
141
|
action_kind: str = "tool"
|
|
141
142
|
server_name: str = ""
|
|
143
|
+
preflight: Mapping[str, Any] | None = None
|
|
142
144
|
|
|
143
145
|
|
|
144
146
|
@dataclass(frozen=True)
|
|
@@ -22,14 +22,18 @@ from .config import (
|
|
|
22
22
|
provider_info_for,
|
|
23
23
|
settings_to_toml_dict,
|
|
24
24
|
thinking_modes_for_provider,
|
|
25
|
+
ui_setup_from_selection,
|
|
26
|
+
ui_setup_number,
|
|
25
27
|
ui_theme_from_selection,
|
|
26
28
|
ui_theme_number,
|
|
27
29
|
update_config_theme,
|
|
30
|
+
update_config_ui_choice,
|
|
28
31
|
write_config,
|
|
29
32
|
)
|
|
30
33
|
from .config.settings import DEFAULT_UI_THEME, UI_THEMES
|
|
31
34
|
from .errors import format_error_display
|
|
32
35
|
from .llm.cache_context import format_cache_usage
|
|
36
|
+
from .llm.multimodal import redact_image_data_urls
|
|
33
37
|
from .llm.provider import build_provider_bundle
|
|
34
38
|
from .llm.runner import DEFAULT_MAX_TURNS, run_prompt_once
|
|
35
39
|
from .sessions import DeepySession, list_session_entries
|
|
@@ -85,7 +89,7 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
85
89
|
run_parser.add_argument("--session", help="Resume an existing session id.")
|
|
86
90
|
run_parser.add_argument("--skill", action="append", default=[], help="Load a skill by name.")
|
|
87
91
|
|
|
88
|
-
subparsers.add_parser("tui", help="Start the
|
|
92
|
+
subparsers.add_parser("tui", help="Start the Modern UI.")
|
|
89
93
|
|
|
90
94
|
sessions_parser = subparsers.add_parser("sessions", help="Inspect project sessions.")
|
|
91
95
|
sessions_sub = sessions_parser.add_subparsers(dest="sessions_command", required=True)
|
|
@@ -130,6 +134,7 @@ def _cmd_config_init(args: argparse.Namespace) -> int:
|
|
|
130
134
|
base_url=args.base_url,
|
|
131
135
|
thinking_mode=args.thinking,
|
|
132
136
|
theme=args.theme,
|
|
137
|
+
interface="classic",
|
|
133
138
|
)
|
|
134
139
|
print(f"Wrote {config_path}")
|
|
135
140
|
return 0
|
|
@@ -169,7 +174,10 @@ def _run_config_setup(config_path: Path) -> None:
|
|
|
169
174
|
)
|
|
170
175
|
base_url = _prompt_config_value("Base URL", default=base_default)
|
|
171
176
|
thinking_mode = _prompt_thinking_mode_value(provider, default=existing.model.reasoning_mode)
|
|
172
|
-
theme =
|
|
177
|
+
interface, theme = _prompt_ui_choice_value(
|
|
178
|
+
default_interface=existing.ui.interface,
|
|
179
|
+
default_theme=existing.ui.theme,
|
|
180
|
+
)
|
|
173
181
|
_write_config(
|
|
174
182
|
config_path,
|
|
175
183
|
api_key=api_key,
|
|
@@ -178,6 +186,7 @@ def _run_config_setup(config_path: Path) -> None:
|
|
|
178
186
|
base_url=base_url,
|
|
179
187
|
thinking_mode=thinking_mode,
|
|
180
188
|
theme=theme,
|
|
189
|
+
interface=interface,
|
|
181
190
|
)
|
|
182
191
|
|
|
183
192
|
|
|
@@ -388,6 +397,27 @@ def _prompt_theme_value(*, default: str = DEFAULT_UI_THEME) -> str:
|
|
|
388
397
|
return ui_theme_from_selection(value, default=default)
|
|
389
398
|
|
|
390
399
|
|
|
400
|
+
def _prompt_ui_choice_value(
|
|
401
|
+
*,
|
|
402
|
+
default_interface: str = "classic",
|
|
403
|
+
default_theme: str = DEFAULT_UI_THEME,
|
|
404
|
+
) -> tuple[str, str]:
|
|
405
|
+
print("UI:")
|
|
406
|
+
print("1. Classic UI + dark theme Default terminal UI")
|
|
407
|
+
print("2. Classic UI + light theme")
|
|
408
|
+
print("3. Modern UI + dark theme Textual UI")
|
|
409
|
+
print("4. Modern UI + light theme Textual UI")
|
|
410
|
+
value = _prompt_config_value(
|
|
411
|
+
"UI number",
|
|
412
|
+
default=ui_setup_number(default_interface, default_theme),
|
|
413
|
+
)
|
|
414
|
+
return ui_setup_from_selection(
|
|
415
|
+
value,
|
|
416
|
+
default_interface=default_interface,
|
|
417
|
+
default_theme=default_theme,
|
|
418
|
+
)
|
|
419
|
+
|
|
420
|
+
|
|
391
421
|
def _write_config(
|
|
392
422
|
config_path: Path,
|
|
393
423
|
*,
|
|
@@ -396,6 +426,7 @@ def _write_config(
|
|
|
396
426
|
model: str,
|
|
397
427
|
base_url: str | None,
|
|
398
428
|
theme: str,
|
|
429
|
+
interface: str,
|
|
399
430
|
thinking_mode: str | None,
|
|
400
431
|
) -> None:
|
|
401
432
|
write_config(
|
|
@@ -405,6 +436,7 @@ def _write_config(
|
|
|
405
436
|
model=model,
|
|
406
437
|
base_url=base_url,
|
|
407
438
|
theme=theme,
|
|
439
|
+
interface=interface,
|
|
408
440
|
thinking_mode=thinking_mode,
|
|
409
441
|
)
|
|
410
442
|
|
|
@@ -614,7 +646,7 @@ def _cmd_sessions(args: argparse.Namespace) -> int:
|
|
|
614
646
|
else 0,
|
|
615
647
|
"cache_break_reason": entry.cache_break_reason if entry is not None else None,
|
|
616
648
|
"cache_usage": entry.cache_usage if entry is not None else None,
|
|
617
|
-
"items": items,
|
|
649
|
+
"items": redact_image_data_urls(items),
|
|
618
650
|
}
|
|
619
651
|
)
|
|
620
652
|
)
|
|
@@ -669,21 +701,35 @@ def _ensure_interactive_settings(args: argparse.Namespace) -> Settings:
|
|
|
669
701
|
raise SystemExit(1)
|
|
670
702
|
settings = load_settings(args.config)
|
|
671
703
|
if settings.path is not None and not settings.ui.theme_configured:
|
|
672
|
-
theme =
|
|
673
|
-
|
|
704
|
+
interface, theme = _prompt_ui_choice_value(
|
|
705
|
+
default_interface=settings.ui.interface,
|
|
706
|
+
default_theme=settings.ui.theme,
|
|
707
|
+
)
|
|
708
|
+
update_config_ui_choice(settings.path, interface=interface, theme=theme)
|
|
674
709
|
settings = load_settings(args.config)
|
|
675
710
|
return settings
|
|
676
711
|
|
|
677
712
|
|
|
678
713
|
def _cmd_tui(args: argparse.Namespace) -> int:
|
|
679
714
|
if not sys.stdin.isatty():
|
|
680
|
-
print("
|
|
715
|
+
print("Modern UI requires a TTY; use `deepy run` for non-interactive prompts.", file=sys.stderr)
|
|
681
716
|
return 1
|
|
682
717
|
from deepy.tui import run_tui
|
|
683
718
|
|
|
684
719
|
return run_tui(_ensure_interactive_settings(args), project_root=Path.cwd())
|
|
685
720
|
|
|
686
721
|
|
|
722
|
+
def _cmd_interactive(args: argparse.Namespace) -> int:
|
|
723
|
+
settings = _ensure_interactive_settings(args)
|
|
724
|
+
if settings.ui.interface == "modern":
|
|
725
|
+
if not sys.stdin.isatty():
|
|
726
|
+
raise RuntimeError("Modern UI requires a TTY.")
|
|
727
|
+
from deepy.tui import run_tui
|
|
728
|
+
|
|
729
|
+
return run_tui(settings, project_root=Path.cwd())
|
|
730
|
+
return run_interactive(settings)
|
|
731
|
+
|
|
732
|
+
|
|
687
733
|
def main(argv: Sequence[str] | None = None) -> int:
|
|
688
734
|
parser = _build_parser()
|
|
689
735
|
args = parser.parse_args(argv)
|
|
@@ -714,7 +760,7 @@ def main(argv: Sequence[str] | None = None) -> int:
|
|
|
714
760
|
|
|
715
761
|
if not sys.stdin.isatty():
|
|
716
762
|
parser.error("interactive mode requires a TTY; use `deepy doctor` or `deepy config show`.")
|
|
717
|
-
return
|
|
763
|
+
return _cmd_interactive(args)
|
|
718
764
|
|
|
719
765
|
|
|
720
766
|
if __name__ == "__main__":
|
|
@@ -6,6 +6,7 @@ from .settings import (
|
|
|
6
6
|
DEFAULT_BASE_URL,
|
|
7
7
|
DEFAULT_COMPACT_PRESERVE_RECENT_MESSAGES,
|
|
8
8
|
DEFAULT_INPUT_SUGGESTIONS_ENABLED,
|
|
9
|
+
DEFAULT_UI_INTERFACE,
|
|
9
10
|
DEFAULT_OPENROUTER_BASE_URL,
|
|
10
11
|
DEFAULT_PROVIDER,
|
|
11
12
|
DEFAULT_RESERVED_CONTEXT_TOKENS,
|
|
@@ -27,6 +28,9 @@ from .settings import (
|
|
|
27
28
|
TestShellToolConfig,
|
|
28
29
|
UI_THEME_OPTIONS,
|
|
29
30
|
UI_THEMES,
|
|
31
|
+
UI_INTERFACE_OPTIONS,
|
|
32
|
+
UI_INTERFACES,
|
|
33
|
+
UI_SETUP_OPTIONS,
|
|
30
34
|
UI_VIEW_MODES,
|
|
31
35
|
ToolsConfig,
|
|
32
36
|
UiConfig,
|
|
@@ -45,6 +49,7 @@ from .settings import (
|
|
|
45
49
|
is_valid_thinking_mode,
|
|
46
50
|
is_valid_thinking_mode_for_provider,
|
|
47
51
|
is_valid_ui_theme,
|
|
52
|
+
is_valid_ui_interface,
|
|
48
53
|
is_valid_ui_view_mode,
|
|
49
54
|
is_valid_reasoning_mode,
|
|
50
55
|
is_valid_config_audit_mode,
|
|
@@ -57,8 +62,15 @@ from .settings import (
|
|
|
57
62
|
update_config_model_settings,
|
|
58
63
|
update_config_audit_mode,
|
|
59
64
|
update_config_input_suggestions_enabled,
|
|
65
|
+
update_config_textual_theme,
|
|
60
66
|
update_config_theme,
|
|
67
|
+
update_config_ui_choice,
|
|
68
|
+
update_config_ui_interface,
|
|
61
69
|
update_config_view_mode,
|
|
70
|
+
ui_interface_from_selection,
|
|
71
|
+
ui_interface_number,
|
|
72
|
+
ui_setup_from_selection,
|
|
73
|
+
ui_setup_number,
|
|
62
74
|
ui_theme_from_selection,
|
|
63
75
|
ui_theme_number,
|
|
64
76
|
write_config,
|
|
@@ -70,6 +82,7 @@ __all__ = [
|
|
|
70
82
|
"DEFAULT_BASE_URL",
|
|
71
83
|
"DEFAULT_COMPACT_PRESERVE_RECENT_MESSAGES",
|
|
72
84
|
"DEFAULT_INPUT_SUGGESTIONS_ENABLED",
|
|
85
|
+
"DEFAULT_UI_INTERFACE",
|
|
73
86
|
"DEFAULT_OPENROUTER_BASE_URL",
|
|
74
87
|
"DEFAULT_PROVIDER",
|
|
75
88
|
"DEFAULT_RESERVED_CONTEXT_TOKENS",
|
|
@@ -91,6 +104,9 @@ __all__ = [
|
|
|
91
104
|
"TestShellToolConfig",
|
|
92
105
|
"UI_THEME_OPTIONS",
|
|
93
106
|
"UI_THEMES",
|
|
107
|
+
"UI_INTERFACE_OPTIONS",
|
|
108
|
+
"UI_INTERFACES",
|
|
109
|
+
"UI_SETUP_OPTIONS",
|
|
94
110
|
"UI_VIEW_MODES",
|
|
95
111
|
"ToolsConfig",
|
|
96
112
|
"UiConfig",
|
|
@@ -109,6 +125,7 @@ __all__ = [
|
|
|
109
125
|
"is_valid_thinking_mode",
|
|
110
126
|
"is_valid_thinking_mode_for_provider",
|
|
111
127
|
"is_valid_ui_theme",
|
|
128
|
+
"is_valid_ui_interface",
|
|
112
129
|
"is_valid_ui_view_mode",
|
|
113
130
|
"is_valid_reasoning_mode",
|
|
114
131
|
"is_valid_config_audit_mode",
|
|
@@ -121,8 +138,15 @@ __all__ = [
|
|
|
121
138
|
"update_config_model_settings",
|
|
122
139
|
"update_config_audit_mode",
|
|
123
140
|
"update_config_input_suggestions_enabled",
|
|
141
|
+
"update_config_textual_theme",
|
|
124
142
|
"update_config_theme",
|
|
143
|
+
"update_config_ui_choice",
|
|
144
|
+
"update_config_ui_interface",
|
|
125
145
|
"update_config_view_mode",
|
|
146
|
+
"ui_interface_from_selection",
|
|
147
|
+
"ui_interface_number",
|
|
148
|
+
"ui_setup_from_selection",
|
|
149
|
+
"ui_setup_number",
|
|
126
150
|
"ui_theme_from_selection",
|
|
127
151
|
"ui_theme_number",
|
|
128
152
|
"write_config",
|
|
@@ -19,6 +19,7 @@ DEFAULT_RESERVED_CONTEXT_TOKENS = 50_000
|
|
|
19
19
|
DEFAULT_COMPACT_PRESERVE_RECENT_MESSAGES = 2
|
|
20
20
|
DEFAULT_WEB_SEARCH_SEARXNG_URL = "https://s.kirineko.tech/"
|
|
21
21
|
DEFAULT_UI_THEME = "dark"
|
|
22
|
+
DEFAULT_UI_INTERFACE = "classic"
|
|
22
23
|
DEFAULT_MCP_ENABLED = True
|
|
23
24
|
DEFAULT_MCP_CONNECT_TIMEOUT_SECONDS = 10.0
|
|
24
25
|
DEFAULT_MCP_CLEANUP_TIMEOUT_SECONDS = 10.0
|
|
@@ -50,6 +51,14 @@ THINKING_MODES = set(DEEPSEEK_REASONING_MODES) | set(SWITCH_ONLY_THINKING_MODES)
|
|
|
50
51
|
PROVIDERS = {"deepseek", "openrouter", "xiaomi"}
|
|
51
52
|
UI_THEMES = {"dark", "light"}
|
|
52
53
|
UI_THEME_OPTIONS = (("1", "dark"), ("2", "light"))
|
|
54
|
+
UI_INTERFACES = {"classic", "modern"}
|
|
55
|
+
UI_INTERFACE_OPTIONS = (("1", "classic"), ("2", "modern"))
|
|
56
|
+
UI_SETUP_OPTIONS = (
|
|
57
|
+
("1", "classic", "dark"),
|
|
58
|
+
("2", "classic", "light"),
|
|
59
|
+
("3", "modern", "dark"),
|
|
60
|
+
("4", "modern", "light"),
|
|
61
|
+
)
|
|
53
62
|
UI_VIEW_MODES = {"concise", "full"}
|
|
54
63
|
|
|
55
64
|
|
|
@@ -59,6 +68,7 @@ class ModelInfo:
|
|
|
59
68
|
label: str
|
|
60
69
|
description: str
|
|
61
70
|
supports_thinking: bool = True
|
|
71
|
+
supports_image_input: bool = False
|
|
62
72
|
default_reasoning_mode: str = "max"
|
|
63
73
|
|
|
64
74
|
|
|
@@ -102,6 +112,7 @@ OPENROUTER_MODEL_CATALOG = (
|
|
|
102
112
|
name="xiaomi/mimo-v2.5",
|
|
103
113
|
label="MiMo V2.5",
|
|
104
114
|
description="Xiaomi MiMo V2.5 via OpenRouter.",
|
|
115
|
+
supports_image_input=True,
|
|
105
116
|
default_reasoning_mode="enabled",
|
|
106
117
|
),
|
|
107
118
|
)
|
|
@@ -116,6 +127,7 @@ XIAOMI_MODEL_CATALOG = (
|
|
|
116
127
|
name="mimo-v2.5",
|
|
117
128
|
label="MiMo V2.5",
|
|
118
129
|
description="Xiaomi official MiMo V2.5.",
|
|
130
|
+
supports_image_input=True,
|
|
119
131
|
default_reasoning_mode="enabled",
|
|
120
132
|
),
|
|
121
133
|
)
|
|
@@ -327,6 +339,10 @@ def _as_str(value: Any, default: str = "") -> str:
|
|
|
327
339
|
return value.strip() if isinstance(value, str) and value.strip() else default
|
|
328
340
|
|
|
329
341
|
|
|
342
|
+
def _as_optional_str(value: Any) -> str | None:
|
|
343
|
+
return value.strip() if isinstance(value, str) and value.strip() else None
|
|
344
|
+
|
|
345
|
+
|
|
330
346
|
def _as_string_tuple(value: Any) -> tuple[str, ...]:
|
|
331
347
|
if not isinstance(value, list):
|
|
332
348
|
return ()
|
|
@@ -564,13 +580,19 @@ class McpConfig:
|
|
|
564
580
|
@dataclass(frozen=True)
|
|
565
581
|
class UiConfig:
|
|
566
582
|
theme: str = DEFAULT_UI_THEME
|
|
583
|
+
interface: str = DEFAULT_UI_INTERFACE
|
|
567
584
|
theme_configured: bool = False
|
|
585
|
+
textual_theme: str | None = None
|
|
568
586
|
input_suggestions_enabled: bool = DEFAULT_INPUT_SUGGESTIONS_ENABLED
|
|
569
587
|
view_mode: str = DEFAULT_UI_VIEW_MODE
|
|
570
588
|
|
|
571
589
|
@classmethod
|
|
572
590
|
def from_mapping(cls, raw: Mapping[str, Any]) -> Self:
|
|
573
591
|
theme = raw.get("theme")
|
|
592
|
+
interface = _as_str(raw.get("interface"), DEFAULT_UI_INTERFACE)
|
|
593
|
+
if interface not in UI_INTERFACES:
|
|
594
|
+
interface = DEFAULT_UI_INTERFACE
|
|
595
|
+
textual_theme = _as_optional_str(raw.get("textual_theme"))
|
|
574
596
|
input_suggestions_enabled = _as_bool(
|
|
575
597
|
raw.get("input_suggestions_enabled"),
|
|
576
598
|
DEFAULT_INPUT_SUGGESTIONS_ENABLED,
|
|
@@ -581,18 +603,27 @@ class UiConfig:
|
|
|
581
603
|
if isinstance(theme, str) and theme.strip() == "auto":
|
|
582
604
|
return cls(
|
|
583
605
|
theme=DEFAULT_UI_THEME,
|
|
606
|
+
interface=interface,
|
|
584
607
|
theme_configured=True,
|
|
608
|
+
textual_theme=textual_theme,
|
|
585
609
|
input_suggestions_enabled=input_suggestions_enabled,
|
|
586
610
|
view_mode=view_mode,
|
|
587
611
|
)
|
|
588
612
|
if isinstance(theme, str) and theme.strip() in UI_THEMES:
|
|
589
613
|
return cls(
|
|
590
614
|
theme=theme.strip(),
|
|
615
|
+
interface=interface,
|
|
591
616
|
theme_configured=True,
|
|
617
|
+
textual_theme=textual_theme,
|
|
592
618
|
input_suggestions_enabled=input_suggestions_enabled,
|
|
593
619
|
view_mode=view_mode,
|
|
594
620
|
)
|
|
595
|
-
return cls(
|
|
621
|
+
return cls(
|
|
622
|
+
textual_theme=textual_theme,
|
|
623
|
+
interface=interface,
|
|
624
|
+
input_suggestions_enabled=input_suggestions_enabled,
|
|
625
|
+
view_mode=view_mode,
|
|
626
|
+
)
|
|
596
627
|
|
|
597
628
|
|
|
598
629
|
@dataclass(frozen=True)
|
|
@@ -669,6 +700,10 @@ def is_valid_ui_theme(value: str) -> bool:
|
|
|
669
700
|
return value in UI_THEMES
|
|
670
701
|
|
|
671
702
|
|
|
703
|
+
def is_valid_ui_interface(value: str) -> bool:
|
|
704
|
+
return value in UI_INTERFACES
|
|
705
|
+
|
|
706
|
+
|
|
672
707
|
def is_valid_ui_view_mode(value: str) -> bool:
|
|
673
708
|
return value in UI_VIEW_MODES
|
|
674
709
|
|
|
@@ -713,6 +748,52 @@ def ui_theme_from_selection(value: str, *, default: str = DEFAULT_UI_THEME) -> s
|
|
|
713
748
|
return default if is_valid_ui_theme(default) else DEFAULT_UI_THEME
|
|
714
749
|
|
|
715
750
|
|
|
751
|
+
def ui_interface_number(interface: str) -> str:
|
|
752
|
+
for number, value in UI_INTERFACE_OPTIONS:
|
|
753
|
+
if value == interface:
|
|
754
|
+
return number
|
|
755
|
+
return "1"
|
|
756
|
+
|
|
757
|
+
|
|
758
|
+
def ui_interface_from_selection(value: str, *, default: str = DEFAULT_UI_INTERFACE) -> str:
|
|
759
|
+
normalized = value.strip().lower()
|
|
760
|
+
if not normalized:
|
|
761
|
+
return default if is_valid_ui_interface(default) else DEFAULT_UI_INTERFACE
|
|
762
|
+
if normalized in UI_INTERFACES:
|
|
763
|
+
return normalized
|
|
764
|
+
by_number = dict(UI_INTERFACE_OPTIONS)
|
|
765
|
+
selected = by_number.get(normalized)
|
|
766
|
+
if selected is not None:
|
|
767
|
+
return selected
|
|
768
|
+
return default if is_valid_ui_interface(default) else DEFAULT_UI_INTERFACE
|
|
769
|
+
|
|
770
|
+
|
|
771
|
+
def ui_setup_number(interface: str, theme: str) -> str:
|
|
772
|
+
for number, option_interface, option_theme in UI_SETUP_OPTIONS:
|
|
773
|
+
if option_interface == interface and option_theme == theme:
|
|
774
|
+
return number
|
|
775
|
+
return "1"
|
|
776
|
+
|
|
777
|
+
|
|
778
|
+
def ui_setup_from_selection(
|
|
779
|
+
value: str,
|
|
780
|
+
*,
|
|
781
|
+
default_interface: str = DEFAULT_UI_INTERFACE,
|
|
782
|
+
default_theme: str = DEFAULT_UI_THEME,
|
|
783
|
+
) -> tuple[str, str]:
|
|
784
|
+
normalized = value.strip().lower()
|
|
785
|
+
fallback = (
|
|
786
|
+
default_interface if is_valid_ui_interface(default_interface) else DEFAULT_UI_INTERFACE,
|
|
787
|
+
default_theme if is_valid_ui_theme(default_theme) else DEFAULT_UI_THEME,
|
|
788
|
+
)
|
|
789
|
+
if not normalized:
|
|
790
|
+
return fallback
|
|
791
|
+
for number, option_interface, option_theme in UI_SETUP_OPTIONS:
|
|
792
|
+
if normalized in {number, f"{option_interface}-{option_theme}", f"{option_interface} {option_theme}"}:
|
|
793
|
+
return option_interface, option_theme
|
|
794
|
+
return fallback
|
|
795
|
+
|
|
796
|
+
|
|
716
797
|
def write_config(
|
|
717
798
|
config_path: Path,
|
|
718
799
|
*,
|
|
@@ -721,10 +802,13 @@ def write_config(
|
|
|
721
802
|
model: str,
|
|
722
803
|
base_url: str | None = None,
|
|
723
804
|
theme: str,
|
|
805
|
+
interface: str = DEFAULT_UI_INTERFACE,
|
|
724
806
|
thinking_mode: str | None = None,
|
|
725
807
|
) -> None:
|
|
726
808
|
if not is_valid_ui_theme(theme):
|
|
727
809
|
raise ValueError("UI theme must be one of: dark, light.")
|
|
810
|
+
if not is_valid_ui_interface(interface):
|
|
811
|
+
raise ValueError("UI interface must be one of: classic, modern.")
|
|
728
812
|
if not is_supported_provider(provider):
|
|
729
813
|
raise ValueError("Provider must be one of: deepseek, openrouter, xiaomi.")
|
|
730
814
|
provider_info = provider_info_for(provider)
|
|
@@ -788,6 +872,7 @@ def write_config(
|
|
|
788
872
|
},
|
|
789
873
|
},
|
|
790
874
|
"ui": {
|
|
875
|
+
"interface": interface,
|
|
791
876
|
"theme": theme,
|
|
792
877
|
"input_suggestions_enabled": DEFAULT_INPUT_SUGGESTIONS_ENABLED,
|
|
793
878
|
"view_mode": DEFAULT_UI_VIEW_MODE,
|
|
@@ -863,6 +948,54 @@ def update_config_theme(config_path: Path, theme: str) -> None:
|
|
|
863
948
|
ui = raw.get("ui")
|
|
864
949
|
ui_map = dict(ui) if isinstance(ui, Mapping) else {}
|
|
865
950
|
ui_map["theme"] = theme
|
|
951
|
+
ui_map.pop("textual_theme", None)
|
|
952
|
+
raw["ui"] = ui_map
|
|
953
|
+
_write_private_toml(path, raw)
|
|
954
|
+
|
|
955
|
+
|
|
956
|
+
def update_config_ui_interface(config_path: Path, interface: str) -> None:
|
|
957
|
+
if not is_valid_ui_interface(interface):
|
|
958
|
+
raise ValueError("UI interface must be one of: classic, modern.")
|
|
959
|
+
path = config_path.expanduser()
|
|
960
|
+
if path.suffix == ".json":
|
|
961
|
+
raise ValueError("Deepy only supports TOML config files; JSON config is not supported.")
|
|
962
|
+
raw = _read_toml_mapping(path)
|
|
963
|
+
ui = raw.get("ui")
|
|
964
|
+
ui_map = dict(ui) if isinstance(ui, Mapping) else {}
|
|
965
|
+
ui_map["interface"] = interface
|
|
966
|
+
raw["ui"] = ui_map
|
|
967
|
+
_write_private_toml(path, raw)
|
|
968
|
+
|
|
969
|
+
|
|
970
|
+
def update_config_ui_choice(config_path: Path, *, interface: str, theme: str) -> None:
|
|
971
|
+
if not is_valid_ui_interface(interface):
|
|
972
|
+
raise ValueError("UI interface must be one of: classic, modern.")
|
|
973
|
+
if not is_valid_ui_theme(theme):
|
|
974
|
+
raise ValueError("UI theme must be one of: dark, light.")
|
|
975
|
+
path = config_path.expanduser()
|
|
976
|
+
if path.suffix == ".json":
|
|
977
|
+
raise ValueError("Deepy only supports TOML config files; JSON config is not supported.")
|
|
978
|
+
raw = _read_toml_mapping(path)
|
|
979
|
+
ui = raw.get("ui")
|
|
980
|
+
ui_map = dict(ui) if isinstance(ui, Mapping) else {}
|
|
981
|
+
ui_map["interface"] = interface
|
|
982
|
+
ui_map["theme"] = theme
|
|
983
|
+
ui_map.pop("textual_theme", None)
|
|
984
|
+
raw["ui"] = ui_map
|
|
985
|
+
_write_private_toml(path, raw)
|
|
986
|
+
|
|
987
|
+
|
|
988
|
+
def update_config_textual_theme(config_path: Path, textual_theme: str) -> None:
|
|
989
|
+
theme = textual_theme.strip()
|
|
990
|
+
if not theme:
|
|
991
|
+
raise ValueError("Textual theme must not be empty.")
|
|
992
|
+
path = config_path.expanduser()
|
|
993
|
+
if path.suffix == ".json":
|
|
994
|
+
raise ValueError("Deepy only supports TOML config files; JSON config is not supported.")
|
|
995
|
+
raw = _read_toml_mapping(path)
|
|
996
|
+
ui = raw.get("ui")
|
|
997
|
+
ui_map = dict(ui) if isinstance(ui, Mapping) else {}
|
|
998
|
+
ui_map["textual_theme"] = theme
|
|
866
999
|
raw["ui"] = ui_map
|
|
867
1000
|
_write_private_toml(path, raw)
|
|
868
1001
|
|