deepy-cli 0.2.28__tar.gz → 0.2.30__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.28 → deepy_cli-0.2.30}/PKG-INFO +1 -1
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/pyproject.toml +1 -1
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/__init__.py +1 -1
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/cli.py +4 -4
- deepy_cli-0.2.30/src/deepy/config/config_io.py +463 -0
- deepy_cli-0.2.30/src/deepy/config/providers.py +343 -0
- deepy_cli-0.2.30/src/deepy/config/schema.py +357 -0
- deepy_cli-0.2.30/src/deepy/config/settings.py +163 -0
- deepy_cli-0.2.30/src/deepy/format_tokens.py +23 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/llm/agent.py +1 -1
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/llm/runner.py +14 -493
- deepy_cli-0.2.30/src/deepy/llm/runner_approvals.py +175 -0
- deepy_cli-0.2.30/src/deepy/llm/runner_errors.py +126 -0
- deepy_cli-0.2.30/src/deepy/llm/runner_interrupt.py +209 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/mcp.py +116 -10
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/sessions/session.py +1 -3
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/status.py +1 -12
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/tools/agents.py +11 -332
- deepy_cli-0.2.30/src/deepy/tools/arg_repair.py +130 -0
- deepy_cli-0.2.30/src/deepy/tools/builtin.py +70 -0
- deepy_cli-0.2.30/src/deepy/tools/constants.py +72 -0
- deepy_cli-0.2.30/src/deepy/tools/media.py +134 -0
- deepy_cli-0.2.30/src/deepy/tools/mutation_policy.py +219 -0
- deepy_cli-0.2.30/src/deepy/tools/payload_parsing.py +210 -0
- deepy_cli-0.2.30/src/deepy/tools/runtime/__init__.py +0 -0
- deepy_cli-0.2.30/src/deepy/tools/runtime/interaction.py +122 -0
- deepy_cli-0.2.30/src/deepy/tools/runtime/mutation_apply.py +315 -0
- deepy_cli-0.2.30/src/deepy/tools/runtime/mutation_preflight.py +444 -0
- deepy_cli-0.2.30/src/deepy/tools/runtime/read.py +234 -0
- deepy_cli-0.2.30/src/deepy/tools/runtime/shell.py +213 -0
- deepy_cli-0.2.30/src/deepy/tools/runtime/state.py +23 -0
- deepy_cli-0.2.30/src/deepy/tools/runtime/tasks.py +83 -0
- deepy_cli-0.2.30/src/deepy/tools/runtime/web.py +297 -0
- deepy_cli-0.2.30/src/deepy/tools/schema_compat.py +95 -0
- deepy_cli-0.2.30/src/deepy/tools/shell_command.py +631 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/tools/test_shell.py +76 -5
- deepy_cli-0.2.30/src/deepy/tools/text_io.py +177 -0
- deepy_cli-0.2.30/src/deepy/tools/text_match.py +212 -0
- deepy_cli-0.2.30/src/deepy/tools/tool_args.py +120 -0
- deepy_cli-0.2.30/src/deepy/tools/tool_dataclasses.py +169 -0
- deepy_cli-0.2.30/src/deepy/tools/web/__init__.py +0 -0
- deepy_cli-0.2.30/src/deepy/tools/web/fetch_html.py +200 -0
- deepy_cli-0.2.30/src/deepy/tools/web/query.py +171 -0
- deepy_cli-0.2.30/src/deepy/tools/web/search_parse.py +176 -0
- deepy_cli-0.2.30/src/deepy/ui/__init__.py +7 -0
- deepy_cli-0.2.30/src/deepy/ui/classic/__init__.py +8 -0
- deepy_cli-0.2.30/src/deepy/ui/classic/approvals.py +262 -0
- deepy_cli-0.2.30/src/deepy/ui/classic/commands/__init__.py +1 -0
- deepy_cli-0.2.30/src/deepy/ui/classic/commands/config_choices.py +140 -0
- deepy_cli-0.2.30/src/deepy/ui/classic/commands/config_commands.py +137 -0
- deepy_cli-0.2.30/src/deepy/ui/classic/commands/config_setup.py +180 -0
- deepy_cli-0.2.30/src/deepy/ui/classic/commands/model_commands.py +351 -0
- deepy_cli-0.2.30/src/deepy/ui/classic/commands/skill_commands.py +334 -0
- deepy_cli-0.2.30/src/deepy/ui/classic/esc_watch.py +138 -0
- deepy_cli-0.2.30/src/deepy/ui/classic/exit_summary.py +93 -0
- deepy_cli-0.2.30/src/deepy/ui/classic/footer.py +228 -0
- {deepy_cli-0.2.28/src/deepy/ui → deepy_cli-0.2.30/src/deepy/ui/classic}/markdown.py +2 -2
- deepy_cli-0.2.30/src/deepy/ui/classic/pickers/__init__.py +1 -0
- deepy_cli-0.2.30/src/deepy/ui/classic/pickers/skill_detail_viewer.py +86 -0
- deepy_cli-0.2.30/src/deepy/ui/classic/pickers/skill_install_scope_picker.py +99 -0
- deepy_cli-0.2.28/src/deepy/ui/skill_picker.py → deepy_cli-0.2.30/src/deepy/ui/classic/pickers/skill_menu_picker.py +12 -291
- deepy_cli-0.2.30/src/deepy/ui/classic/pickers/skill_picker.py +38 -0
- deepy_cli-0.2.30/src/deepy/ui/classic/pickers/skill_picker_types.py +137 -0
- deepy_cli-0.2.30/src/deepy/ui/classic/printing.py +304 -0
- deepy_cli-0.2.30/src/deepy/ui/classic/prompt/__init__.py +1 -0
- {deepy_cli-0.2.28/src/deepy/ui → deepy_cli-0.2.30/src/deepy/ui/classic/prompt}/prompt_input.py +18 -189
- deepy_cli-0.2.30/src/deepy/ui/classic/prompt/prompt_skills.py +32 -0
- deepy_cli-0.2.30/src/deepy/ui/classic/prompt/text_measure.py +68 -0
- deepy_cli-0.2.30/src/deepy/ui/classic/questions.py +117 -0
- deepy_cli-0.2.30/src/deepy/ui/classic/runtime_workers.py +179 -0
- deepy_cli-0.2.30/src/deepy/ui/classic/slash_commands.py +416 -0
- deepy_cli-0.2.30/src/deepy/ui/classic/startup.py +201 -0
- deepy_cli-0.2.30/src/deepy/ui/classic/status/__init__.py +1 -0
- deepy_cli-0.2.30/src/deepy/ui/classic/status/approval_render.py +75 -0
- deepy_cli-0.2.30/src/deepy/ui/classic/status/background_tasks.py +117 -0
- deepy_cli-0.2.30/src/deepy/ui/classic/status/runtime_status.py +311 -0
- {deepy_cli-0.2.28/src/deepy/ui → deepy_cli-0.2.30/src/deepy/ui/classic/status}/status_footer.py +1 -1
- deepy_cli-0.2.30/src/deepy/ui/classic/status/transcript_parse.py +82 -0
- deepy_cli-0.2.30/src/deepy/ui/classic/status_display.py +121 -0
- deepy_cli-0.2.30/src/deepy/ui/classic/stream_render.py +220 -0
- deepy_cli-0.2.30/src/deepy/ui/classic/terminal.py +671 -0
- deepy_cli-0.2.30/src/deepy/ui/classic/terminal_bindings.py +41 -0
- deepy_cli-0.2.30/src/deepy/ui/classic/terminal_patchable.py +11 -0
- deepy_cli-0.2.30/src/deepy/ui/classic/terminal_types.py +33 -0
- {deepy_cli-0.2.28/src/deepy/tui → deepy_cli-0.2.30/src/deepy/ui/modern}/__init__.py +1 -1
- deepy_cli-0.2.30/src/deepy/ui/modern/app.py +516 -0
- deepy_cli-0.2.30/src/deepy/ui/modern/app_bindings.py +34 -0
- deepy_cli-0.2.30/src/deepy/ui/modern/app_commands.py +665 -0
- deepy_cli-0.2.30/src/deepy/ui/modern/app_helpers.py +212 -0
- deepy_cli-0.2.30/src/deepy/ui/modern/app_interaction.py +296 -0
- deepy_cli-0.2.30/src/deepy/ui/modern/app_patchable.py +11 -0
- deepy_cli-0.2.30/src/deepy/ui/modern/app_sessions.py +174 -0
- deepy_cli-0.2.30/src/deepy/ui/modern/app_skills.py +350 -0
- deepy_cli-0.2.30/src/deepy/ui/modern/app_state_proto.py +163 -0
- deepy_cli-0.2.30/src/deepy/ui/modern/app_status.py +188 -0
- deepy_cli-0.2.30/src/deepy/ui/modern/app_streaming.py +275 -0
- deepy_cli-0.2.30/src/deepy/ui/modern/app_styles.py +449 -0
- deepy_cli-0.2.30/src/deepy/ui/modern/app_transcript.py +226 -0
- deepy_cli-0.2.30/src/deepy/ui/modern/app_widgets.py +52 -0
- deepy_cli-0.2.30/src/deepy/ui/modern/background_tasks_tui.py +57 -0
- {deepy_cli-0.2.28/src/deepy/tui → deepy_cli-0.2.30/src/deepy/ui/modern}/commands.py +3 -15
- {deepy_cli-0.2.28/src/deepy/tui → deepy_cli-0.2.30/src/deepy/ui/modern}/interaction_surfaces.py +1 -9
- deepy_cli-0.2.30/src/deepy/ui/modern/render/__init__.py +3 -0
- {deepy_cli-0.2.28/src/deepy/tui → deepy_cli-0.2.30/src/deepy/ui/modern/render}/diff.py +2 -2
- deepy_cli-0.2.30/src/deepy/ui/modern/render/status_format.py +156 -0
- deepy_cli-0.2.30/src/deepy/ui/modern/render/tool_format.py +357 -0
- deepy_cli-0.2.30/src/deepy/ui/modern/render/transcript_items.py +203 -0
- {deepy_cli-0.2.28/src/deepy/tui → deepy_cli-0.2.30/src/deepy/ui/modern}/runner.py +1 -1
- deepy_cli-0.2.30/src/deepy/ui/modern/screens/__init__.py +32 -0
- deepy_cli-0.2.30/src/deepy/ui/modern/screens/choice.py +158 -0
- deepy_cli-0.2.30/src/deepy/ui/modern/screens/config.py +16 -0
- deepy_cli-0.2.30/src/deepy/ui/modern/screens/info.py +50 -0
- deepy_cli-0.2.30/src/deepy/ui/modern/screens/skills.py +329 -0
- deepy_cli-0.2.30/src/deepy/ui/modern/skills_tui.py +85 -0
- deepy_cli-0.2.30/src/deepy/ui/modern/widgets/__init__.py +59 -0
- deepy_cli-0.2.30/src/deepy/ui/modern/widgets/blocks.py +129 -0
- deepy_cli-0.2.30/src/deepy/ui/modern/widgets/decision.py +216 -0
- deepy_cli-0.2.30/src/deepy/ui/modern/widgets/diff.py +63 -0
- deepy_cli-0.2.30/src/deepy/ui/modern/widgets/prompt.py +551 -0
- deepy_cli-0.2.30/src/deepy/ui/modern/widgets/question.py +277 -0
- deepy_cli-0.2.30/src/deepy/ui/modern/widgets/tools.py +227 -0
- deepy_cli-0.2.30/src/deepy/ui/shared/__init__.py +7 -0
- deepy_cli-0.2.30/src/deepy/ui/shared/input/__init__.py +7 -0
- deepy_cli-0.2.28/src/deepy/ui/image_input.py → deepy_cli-0.2.30/src/deepy/ui/shared/input/clipboard.py +12 -161
- deepy_cli-0.2.30/src/deepy/ui/shared/input/commands.py +19 -0
- deepy_cli-0.2.30/src/deepy/ui/shared/input/image_input.py +144 -0
- {deepy_cli-0.2.28/src/deepy/ui → deepy_cli-0.2.30/src/deepy/ui/shared/input}/slash_commands.py +0 -12
- deepy_cli-0.2.30/src/deepy/ui/shared/local_command/__init__.py +277 -0
- deepy_cli-0.2.30/src/deepy/ui/shared/local_command/core.py +225 -0
- deepy_cli-0.2.30/src/deepy/ui/shared/local_command/windows.py +163 -0
- deepy_cli-0.2.30/src/deepy/ui/shared/render/__init__.py +3 -0
- {deepy_cli-0.2.28/src/deepy/ui → deepy_cli-0.2.30/src/deepy/ui/shared/render}/audit_approval_panel.py +2 -2
- deepy_cli-0.2.30/src/deepy/ui/shared/render/diff_highlight.py +146 -0
- deepy_cli-0.2.30/src/deepy/ui/shared/render/diff_preview.py +285 -0
- deepy_cli-0.2.30/src/deepy/ui/shared/render/diff_types.py +24 -0
- deepy_cli-0.2.30/src/deepy/ui/shared/render/message_render.py +157 -0
- deepy_cli-0.2.30/src/deepy/ui/shared/render/message_view.py +204 -0
- {deepy_cli-0.2.28/src/deepy/ui → deepy_cli-0.2.30/src/deepy/ui/shared/render}/styles.py +0 -4
- deepy_cli-0.2.30/src/deepy/ui/shared/render/tool_output.py +369 -0
- deepy_cli-0.2.30/src/deepy/ui/shared/render/tool_snippets.py +214 -0
- deepy_cli-0.2.30/src/deepy/ui/shared/render/tool_text.py +115 -0
- {deepy_cli-0.2.28/src/deepy/ui → deepy_cli-0.2.30/src/deepy/ui/shared/render}/welcome.py +2 -2
- deepy_cli-0.2.30/src/deepy/ui/shared/session/__init__.py +3 -0
- deepy_cli-0.2.30/src/deepy/ui/shared/session/session_list.py +43 -0
- {deepy_cli-0.2.28/src/deepy/ui → deepy_cli-0.2.30/src/deepy/ui/shared/session}/session_picker.py +1 -1
- deepy_cli-0.2.30/src/deepy/ui/shared/session/session_transcript.py +126 -0
- deepy_cli-0.2.30/src/deepy/utils/clock.py +7 -0
- deepy_cli-0.2.28/src/deepy/config/settings.py +0 -1070
- deepy_cli-0.2.28/src/deepy/tools/builtin.py +0 -3846
- deepy_cli-0.2.28/src/deepy/tui/app.py +0 -3534
- deepy_cli-0.2.28/src/deepy/tui/compat.py +0 -7
- deepy_cli-0.2.28/src/deepy/tui/screens.py +0 -715
- deepy_cli-0.2.28/src/deepy/tui/widgets.py +0 -1784
- deepy_cli-0.2.28/src/deepy/ui/__init__.py +0 -5
- deepy_cli-0.2.28/src/deepy/ui/app.py +0 -118
- deepy_cli-0.2.28/src/deepy/ui/loading_text.py +0 -87
- deepy_cli-0.2.28/src/deepy/ui/local_command.py +0 -600
- deepy_cli-0.2.28/src/deepy/ui/message_view.py +0 -1389
- deepy_cli-0.2.28/src/deepy/ui/prompt_buffer.py +0 -176
- deepy_cli-0.2.28/src/deepy/ui/session_list.py +0 -140
- deepy_cli-0.2.28/src/deepy/ui/terminal.py +0 -4416
- deepy_cli-0.2.28/src/deepy/ui/thinking_state.py +0 -29
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/README.md +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/__main__.py +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/audit.py +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/background_tasks.py +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/config/__init__.py +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/data/__init__.py +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/data/skills/skill-creator/SKILL.md +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/data/skills/skill-installer/SKILL.md +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/data/tools/AskUserQuestion.md +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/data/tools/Read.md +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/data/tools/Search.md +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/data/tools/Update.md +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/data/tools/WebFetch.md +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/data/tools/WebSearch.md +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/data/tools/Write.md +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/data/tools/__init__.py +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/data/tools/shell.md +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/data/tools/task_list.md +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/data/tools/task_output.md +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/data/tools/task_stop.md +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/data/tools/test_shell.md +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/data/tools/todo_write.md +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/errors.py +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/input_suggestions.py +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/llm/__init__.py +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/llm/cache_context.py +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/llm/compaction.py +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/llm/context.py +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/llm/events.py +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/llm/model_capabilities.py +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/llm/multimodal.py +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/llm/provider.py +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/llm/replay.py +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/llm/thinking.py +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/prompts/__init__.py +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/prompts/compact.py +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/prompts/init_agents.py +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/prompts/rules.py +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/prompts/runtime_context.py +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/prompts/system.py +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/prompts/tool_docs.py +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/session_cost.py +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/sessions/__init__.py +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/sessions/index.py +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/sessions/manager.py +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/sessions/store_helpers.py +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/skill_market.py +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/skills.py +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/subagents.py +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/todos.py +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/tools/__init__.py +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/tools/file_state.py +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/tools/result.py +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/tools/search.py +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/tools/shell_output.py +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/tools/shell_utils.py +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/types/__init__.py +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/types/sdk.py +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/types/tool_payloads.py +0 -0
- {deepy_cli-0.2.28/src/deepy/ui → deepy_cli-0.2.30/src/deepy/ui/classic/pickers}/audit_approval_picker.py +0 -0
- {deepy_cli-0.2.28/src/deepy/ui → deepy_cli-0.2.30/src/deepy/ui/classic/pickers}/theme_picker.py +0 -0
- {deepy_cli-0.2.28/src/deepy/tui → deepy_cli-0.2.30/src/deepy/ui/modern/render}/transcript.py +0 -0
- {deepy_cli-0.2.28/src/deepy/tui → deepy_cli-0.2.30/src/deepy/ui/modern}/state.py +0 -0
- {deepy_cli-0.2.28/src/deepy/tui → deepy_cli-0.2.30/src/deepy/ui/modern}/theme.py +0 -0
- {deepy_cli-0.2.28/src/deepy/ui → deepy_cli-0.2.30/src/deepy/ui/shared/input}/ask_user_question.py +0 -0
- {deepy_cli-0.2.28/src/deepy/ui → deepy_cli-0.2.30/src/deepy/ui/shared/input}/file_mentions.py +0 -0
- {deepy_cli-0.2.28/src/deepy/ui → deepy_cli-0.2.30/src/deepy/ui/shared}/model_picker.py +0 -0
- {deepy_cli-0.2.28/src/deepy/ui → deepy_cli-0.2.30/src/deepy/ui/shared/render}/exit_summary.py +0 -0
- {deepy_cli-0.2.28/src/deepy/ui → deepy_cli-0.2.30/src/deepy/ui/shared/render}/syntax.py +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/update_check.py +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/usage.py +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/utils/__init__.py +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/utils/debug_logger.py +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/utils/error_logger.py +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/utils/json.py +0 -0
- {deepy_cli-0.2.28 → deepy_cli-0.2.30}/src/deepy/utils/notify.py +0 -0
|
@@ -40,8 +40,8 @@ from .sessions import DeepySession, list_session_entries
|
|
|
40
40
|
from .skills import discover_skills, find_skill, format_skills_for_terminal, read_skill_body
|
|
41
41
|
from .status import build_status_report, format_status_report, status_report_to_dict
|
|
42
42
|
from .usage import TokenUsage, format_usage_line, usage_from_run_result
|
|
43
|
-
from .ui import run_interactive
|
|
44
|
-
from .ui.styles import resolve_ui_palette
|
|
43
|
+
from .ui.classic import run_interactive
|
|
44
|
+
from .ui.shared.render.styles import resolve_ui_palette
|
|
45
45
|
from .utils import json as json_utils
|
|
46
46
|
|
|
47
47
|
|
|
@@ -714,7 +714,7 @@ def _cmd_tui(args: argparse.Namespace) -> int:
|
|
|
714
714
|
if not sys.stdin.isatty():
|
|
715
715
|
print("Modern UI requires a TTY; use `deepy run` for non-interactive prompts.", file=sys.stderr)
|
|
716
716
|
return 1
|
|
717
|
-
from deepy.
|
|
717
|
+
from deepy.ui.modern import run_tui
|
|
718
718
|
|
|
719
719
|
return run_tui(_ensure_interactive_settings(args), project_root=Path.cwd())
|
|
720
720
|
|
|
@@ -724,7 +724,7 @@ def _cmd_interactive(args: argparse.Namespace) -> int:
|
|
|
724
724
|
if settings.ui.interface == "modern":
|
|
725
725
|
if not sys.stdin.isatty():
|
|
726
726
|
raise RuntimeError("Modern UI requires a TTY.")
|
|
727
|
-
from deepy.
|
|
727
|
+
from deepy.ui.modern import run_tui
|
|
728
728
|
|
|
729
729
|
return run_tui(settings, project_root=Path.cwd())
|
|
730
730
|
return run_interactive(settings)
|
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import tomllib
|
|
5
|
+
from dataclasses import asdict
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any, Mapping
|
|
8
|
+
|
|
9
|
+
import tomli_w
|
|
10
|
+
|
|
11
|
+
from deepy.audit import AuditMode, DEFAULT_AUDIT_MODE, is_valid_audit_mode
|
|
12
|
+
|
|
13
|
+
from .providers import (
|
|
14
|
+
DEEPSEEK_REASONING_MODES,
|
|
15
|
+
DEFAULT_COMPACT_PRESERVE_RECENT_MESSAGES,
|
|
16
|
+
DEFAULT_COMPACT_TRIGGER_RATIO,
|
|
17
|
+
DEFAULT_CONTEXT_WINDOW_TOKENS,
|
|
18
|
+
DEFAULT_INPUT_SUGGESTIONS_ENABLED,
|
|
19
|
+
DEFAULT_MCP_CACHE_TOOLS_LIST,
|
|
20
|
+
DEFAULT_MCP_CLEANUP_TIMEOUT_SECONDS,
|
|
21
|
+
DEFAULT_MCP_CLIENT_SESSION_TIMEOUT_SECONDS,
|
|
22
|
+
DEFAULT_MCP_CONNECT_TIMEOUT_SECONDS,
|
|
23
|
+
DEFAULT_MCP_ENABLED,
|
|
24
|
+
DEFAULT_PROVIDER,
|
|
25
|
+
DEFAULT_RESERVED_CONTEXT_TOKENS,
|
|
26
|
+
DEFAULT_UI_INTERFACE,
|
|
27
|
+
DEFAULT_UI_THEME,
|
|
28
|
+
DEFAULT_UI_VIEW_MODE,
|
|
29
|
+
DEFAULT_WEB_SEARCH_SEARXNG_URL,
|
|
30
|
+
REASONING_MODES,
|
|
31
|
+
SUPPORTED_DEEPSEEK_MODELS,
|
|
32
|
+
THINKING_MODES,
|
|
33
|
+
UI_INTERFACES,
|
|
34
|
+
UI_INTERFACE_OPTIONS,
|
|
35
|
+
UI_SETUP_OPTIONS,
|
|
36
|
+
UI_THEME_OPTIONS,
|
|
37
|
+
UI_THEMES,
|
|
38
|
+
UI_VIEW_MODES,
|
|
39
|
+
default_config_path,
|
|
40
|
+
is_supported_model_for_provider,
|
|
41
|
+
is_supported_provider,
|
|
42
|
+
is_valid_config_model_for_provider,
|
|
43
|
+
is_valid_thinking_mode_for_provider,
|
|
44
|
+
mask_secret,
|
|
45
|
+
provider_info_for,
|
|
46
|
+
reasoning_effort_for_mode,
|
|
47
|
+
thinking_enabled_for_mode,
|
|
48
|
+
)
|
|
49
|
+
from .schema import (
|
|
50
|
+
ModelConfig,
|
|
51
|
+
Settings,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def load_settings(
|
|
56
|
+
path: str | os.PathLike[str] | None = None,
|
|
57
|
+
*,
|
|
58
|
+
env: Mapping[str, str] | None = None,
|
|
59
|
+
) -> Settings:
|
|
60
|
+
config_path = Path(path).expanduser() if path is not None else default_config_path()
|
|
61
|
+
if config_path.suffix == ".json":
|
|
62
|
+
raise ValueError("Deepy only supports TOML config files; JSON config is not supported.")
|
|
63
|
+
env = env or os.environ
|
|
64
|
+
if not config_path.exists():
|
|
65
|
+
return Settings.from_mapping({}, path=config_path, env=env)
|
|
66
|
+
|
|
67
|
+
with config_path.open("rb") as fh:
|
|
68
|
+
raw = tomllib.load(fh)
|
|
69
|
+
return Settings.from_mapping(raw, path=config_path, env=env)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def settings_to_toml_dict(settings: Settings, *, reveal_secret: bool = False) -> dict[str, Any]:
|
|
73
|
+
data = _drop_empty(asdict(settings))
|
|
74
|
+
data.pop("path", None)
|
|
75
|
+
if "ui" in data:
|
|
76
|
+
data["ui"].pop("theme_configured", None)
|
|
77
|
+
if "audit" in data:
|
|
78
|
+
data["audit"].pop("invalid_mode", None)
|
|
79
|
+
if "mode" in data["audit"] and isinstance(settings.audit.mode, AuditMode):
|
|
80
|
+
data["audit"]["mode"] = settings.audit.mode.value
|
|
81
|
+
if "mcp_safe_tools" in data["audit"]:
|
|
82
|
+
data["audit"]["mcp_safe_tools"] = [
|
|
83
|
+
{"server": item.server, "tool": item.tool} for item in settings.audit.mcp_safe_tools
|
|
84
|
+
]
|
|
85
|
+
api_key = settings.model.api_key
|
|
86
|
+
if api_key:
|
|
87
|
+
data["model"]["api_key"] = api_key if reveal_secret else mask_secret(api_key)
|
|
88
|
+
data["model"]["thinking"] = settings.model.thinking_enabled
|
|
89
|
+
return _drop_empty(data)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def is_valid_ui_theme(value: str) -> bool:
|
|
93
|
+
return value in UI_THEMES
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def is_valid_ui_interface(value: str) -> bool:
|
|
97
|
+
return value in UI_INTERFACES
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def is_valid_ui_view_mode(value: str) -> bool:
|
|
101
|
+
return value in UI_VIEW_MODES
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def is_valid_config_audit_mode(value: str) -> bool:
|
|
105
|
+
return is_valid_audit_mode(value)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def is_supported_deepseek_model(value: str) -> bool:
|
|
109
|
+
return value in SUPPORTED_DEEPSEEK_MODELS
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def is_supported_model(value: str, provider: str) -> bool:
|
|
113
|
+
return is_supported_model_for_provider(value, provider)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def is_valid_reasoning_mode(value: str) -> bool:
|
|
117
|
+
return value in REASONING_MODES
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def is_valid_thinking_mode(value: str) -> bool:
|
|
121
|
+
return value in THINKING_MODES
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def ui_theme_number(theme: str) -> str:
|
|
125
|
+
for number, value in UI_THEME_OPTIONS:
|
|
126
|
+
if value == theme:
|
|
127
|
+
return number
|
|
128
|
+
return "1"
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def ui_theme_from_selection(value: str, *, default: str = DEFAULT_UI_THEME) -> str:
|
|
132
|
+
normalized = value.strip().lower()
|
|
133
|
+
if not normalized:
|
|
134
|
+
return default if is_valid_ui_theme(default) else DEFAULT_UI_THEME
|
|
135
|
+
if normalized in UI_THEMES:
|
|
136
|
+
return normalized
|
|
137
|
+
by_number = dict(UI_THEME_OPTIONS)
|
|
138
|
+
selected = by_number.get(normalized)
|
|
139
|
+
if selected is not None:
|
|
140
|
+
return selected
|
|
141
|
+
return default if is_valid_ui_theme(default) else DEFAULT_UI_THEME
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def ui_interface_number(interface: str) -> str:
|
|
145
|
+
for number, value in UI_INTERFACE_OPTIONS:
|
|
146
|
+
if value == interface:
|
|
147
|
+
return number
|
|
148
|
+
return "1"
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def ui_interface_from_selection(value: str, *, default: str = DEFAULT_UI_INTERFACE) -> str:
|
|
152
|
+
normalized = value.strip().lower()
|
|
153
|
+
if not normalized:
|
|
154
|
+
return default if is_valid_ui_interface(default) else DEFAULT_UI_INTERFACE
|
|
155
|
+
if normalized in UI_INTERFACES:
|
|
156
|
+
return normalized
|
|
157
|
+
by_number = dict(UI_INTERFACE_OPTIONS)
|
|
158
|
+
selected = by_number.get(normalized)
|
|
159
|
+
if selected is not None:
|
|
160
|
+
return selected
|
|
161
|
+
return default if is_valid_ui_interface(default) else DEFAULT_UI_INTERFACE
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def ui_setup_number(interface: str, theme: str) -> str:
|
|
165
|
+
for number, option_interface, option_theme in UI_SETUP_OPTIONS:
|
|
166
|
+
if option_interface == interface and option_theme == theme:
|
|
167
|
+
return number
|
|
168
|
+
return "1"
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def ui_setup_from_selection(
|
|
172
|
+
value: str,
|
|
173
|
+
*,
|
|
174
|
+
default_interface: str = DEFAULT_UI_INTERFACE,
|
|
175
|
+
default_theme: str = DEFAULT_UI_THEME,
|
|
176
|
+
) -> tuple[str, str]:
|
|
177
|
+
normalized = value.strip().lower()
|
|
178
|
+
fallback = (
|
|
179
|
+
default_interface if is_valid_ui_interface(default_interface) else DEFAULT_UI_INTERFACE,
|
|
180
|
+
default_theme if is_valid_ui_theme(default_theme) else DEFAULT_UI_THEME,
|
|
181
|
+
)
|
|
182
|
+
if not normalized:
|
|
183
|
+
return fallback
|
|
184
|
+
for number, option_interface, option_theme in UI_SETUP_OPTIONS:
|
|
185
|
+
if normalized in {number, f"{option_interface}-{option_theme}", f"{option_interface} {option_theme}"}:
|
|
186
|
+
return option_interface, option_theme
|
|
187
|
+
return fallback
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def write_config(
|
|
191
|
+
config_path: Path,
|
|
192
|
+
*,
|
|
193
|
+
api_key: str,
|
|
194
|
+
provider: str = DEFAULT_PROVIDER,
|
|
195
|
+
model: str,
|
|
196
|
+
base_url: str | None = None,
|
|
197
|
+
theme: str,
|
|
198
|
+
interface: str = DEFAULT_UI_INTERFACE,
|
|
199
|
+
thinking_mode: str | None = None,
|
|
200
|
+
) -> None:
|
|
201
|
+
if not is_valid_ui_theme(theme):
|
|
202
|
+
raise ValueError("UI theme must be one of: dark, light.")
|
|
203
|
+
if not is_valid_ui_interface(interface):
|
|
204
|
+
raise ValueError("UI interface must be one of: classic, modern.")
|
|
205
|
+
if not is_supported_provider(provider):
|
|
206
|
+
raise ValueError("Provider must be one of: deepseek, openrouter, xiaomi.")
|
|
207
|
+
provider_info = provider_info_for(provider)
|
|
208
|
+
if not is_valid_config_model_for_provider(model, provider):
|
|
209
|
+
raise ValueError(
|
|
210
|
+
"Model must be one of: " + ", ".join(model_info.name for model_info in provider_info.models)
|
|
211
|
+
)
|
|
212
|
+
mode = thinking_mode or provider_info.default_thinking_mode
|
|
213
|
+
if not is_valid_thinking_mode_for_provider(mode, provider):
|
|
214
|
+
raise ValueError(
|
|
215
|
+
"Thinking mode must be one of: " + ", ".join(provider_info.thinking_modes)
|
|
216
|
+
)
|
|
217
|
+
path = config_path.expanduser()
|
|
218
|
+
if path.suffix == ".json":
|
|
219
|
+
raise ValueError("Deepy only supports TOML config files; JSON config is not supported.")
|
|
220
|
+
resolved_base_url = base_url or provider_info.default_base_url
|
|
221
|
+
payload = {
|
|
222
|
+
"model": {
|
|
223
|
+
"provider": provider,
|
|
224
|
+
"name": model,
|
|
225
|
+
"base_url": resolved_base_url,
|
|
226
|
+
"api_key": api_key,
|
|
227
|
+
"thinking": thinking_enabled_for_mode(mode, provider),
|
|
228
|
+
"reasoning_effort": reasoning_effort_for_mode(mode, provider),
|
|
229
|
+
},
|
|
230
|
+
"audit": {
|
|
231
|
+
"mode": DEFAULT_AUDIT_MODE.value,
|
|
232
|
+
"mcp_safe_tools": [],
|
|
233
|
+
},
|
|
234
|
+
"context": {
|
|
235
|
+
"window_tokens": DEFAULT_CONTEXT_WINDOW_TOKENS,
|
|
236
|
+
"compact_trigger_ratio": DEFAULT_COMPACT_TRIGGER_RATIO,
|
|
237
|
+
"reserved_context_tokens": DEFAULT_RESERVED_CONTEXT_TOKENS,
|
|
238
|
+
"compact_preserve_recent_messages": DEFAULT_COMPACT_PRESERVE_RECENT_MESSAGES,
|
|
239
|
+
},
|
|
240
|
+
"logging": {
|
|
241
|
+
"debug": False,
|
|
242
|
+
},
|
|
243
|
+
"notify": {
|
|
244
|
+
"enabled": False,
|
|
245
|
+
"command": "",
|
|
246
|
+
},
|
|
247
|
+
"tools": {
|
|
248
|
+
"web_search": {
|
|
249
|
+
"searxng_url": DEFAULT_WEB_SEARCH_SEARXNG_URL,
|
|
250
|
+
},
|
|
251
|
+
},
|
|
252
|
+
"mcp": {
|
|
253
|
+
"enabled": DEFAULT_MCP_ENABLED,
|
|
254
|
+
"connect_timeout_seconds": DEFAULT_MCP_CONNECT_TIMEOUT_SECONDS,
|
|
255
|
+
"cleanup_timeout_seconds": DEFAULT_MCP_CLEANUP_TIMEOUT_SECONDS,
|
|
256
|
+
"client_session_timeout_seconds": DEFAULT_MCP_CLIENT_SESSION_TIMEOUT_SECONDS,
|
|
257
|
+
"cache_tools_list": DEFAULT_MCP_CACHE_TOOLS_LIST,
|
|
258
|
+
"allow_project_config": False,
|
|
259
|
+
"prefer_mcp_web_search": True,
|
|
260
|
+
"web_search": {
|
|
261
|
+
"prefer_mcp": True,
|
|
262
|
+
"preferred_server": "",
|
|
263
|
+
"preferred_tools": [],
|
|
264
|
+
"fallback_to_builtin": True,
|
|
265
|
+
},
|
|
266
|
+
},
|
|
267
|
+
"ui": {
|
|
268
|
+
"interface": interface,
|
|
269
|
+
"theme": theme,
|
|
270
|
+
"input_suggestions_enabled": DEFAULT_INPUT_SUGGESTIONS_ENABLED,
|
|
271
|
+
"view_mode": DEFAULT_UI_VIEW_MODE,
|
|
272
|
+
},
|
|
273
|
+
}
|
|
274
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
275
|
+
path.write_text(tomli_w.dumps(payload), encoding="utf-8")
|
|
276
|
+
os.chmod(path, 0o600)
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def update_config_model_settings(
|
|
280
|
+
config_path: Path,
|
|
281
|
+
*,
|
|
282
|
+
provider: str | None = None,
|
|
283
|
+
model: str | None = None,
|
|
284
|
+
base_url: str | None = None,
|
|
285
|
+
reasoning_mode: str | None = None,
|
|
286
|
+
) -> None:
|
|
287
|
+
path = config_path.expanduser()
|
|
288
|
+
if path.suffix == ".json":
|
|
289
|
+
raise ValueError("Deepy only supports TOML config files; JSON config is not supported.")
|
|
290
|
+
raw = _read_toml_mapping(path)
|
|
291
|
+
model_section = raw.get("model")
|
|
292
|
+
model_map = dict(model_section) if isinstance(model_section, Mapping) else {}
|
|
293
|
+
current = ModelConfig.from_mapping(model_map)
|
|
294
|
+
active_provider = provider or current.provider
|
|
295
|
+
if provider is not None and not is_supported_provider(provider):
|
|
296
|
+
raise ValueError("Provider must be one of: deepseek, openrouter, xiaomi.")
|
|
297
|
+
provider_info = provider_info_for(active_provider)
|
|
298
|
+
active_model = model or current.name
|
|
299
|
+
if model is None and provider is not None and not is_supported_model_for_provider(active_model, active_provider):
|
|
300
|
+
active_model = provider_info.default_model
|
|
301
|
+
# `/model` updates intentionally stay within Deepy's curated provider model catalog.
|
|
302
|
+
if not is_supported_model_for_provider(active_model, active_provider):
|
|
303
|
+
raise ValueError(
|
|
304
|
+
"Model must be one of: " + ", ".join(model_info.name for model_info in provider_info.models)
|
|
305
|
+
)
|
|
306
|
+
active_mode = reasoning_mode or (
|
|
307
|
+
current.reasoning_mode
|
|
308
|
+
if is_valid_thinking_mode_for_provider(current.reasoning_mode, active_provider)
|
|
309
|
+
else provider_info.default_thinking_mode
|
|
310
|
+
)
|
|
311
|
+
if not is_valid_thinking_mode_for_provider(active_mode, active_provider):
|
|
312
|
+
if provider_info.thinking_modes == DEEPSEEK_REASONING_MODES:
|
|
313
|
+
raise ValueError("Reasoning mode must be one of: none, high, max.")
|
|
314
|
+
raise ValueError(
|
|
315
|
+
"Thinking mode must be one of: " + ", ".join(provider_info.thinking_modes)
|
|
316
|
+
)
|
|
317
|
+
if provider is not None:
|
|
318
|
+
model_map["provider"] = active_provider
|
|
319
|
+
if base_url is None:
|
|
320
|
+
model_map["base_url"] = provider_info.default_base_url
|
|
321
|
+
if base_url is not None:
|
|
322
|
+
model_map["base_url"] = base_url
|
|
323
|
+
if model is not None:
|
|
324
|
+
model_map["name"] = active_model
|
|
325
|
+
elif provider is not None:
|
|
326
|
+
model_map["name"] = active_model
|
|
327
|
+
if reasoning_mode is not None or provider is not None:
|
|
328
|
+
model_map["thinking"] = thinking_enabled_for_mode(active_mode, active_provider)
|
|
329
|
+
model_map["reasoning_effort"] = reasoning_effort_for_mode(active_mode, active_provider)
|
|
330
|
+
raw["model"] = model_map
|
|
331
|
+
_write_private_toml(path, raw)
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
def update_config_theme(config_path: Path, theme: str) -> None:
|
|
335
|
+
if not is_valid_ui_theme(theme):
|
|
336
|
+
raise ValueError("UI theme must be one of: dark, light.")
|
|
337
|
+
path = config_path.expanduser()
|
|
338
|
+
if path.suffix == ".json":
|
|
339
|
+
raise ValueError("Deepy only supports TOML config files; JSON config is not supported.")
|
|
340
|
+
raw = _read_toml_mapping(path)
|
|
341
|
+
ui = raw.get("ui")
|
|
342
|
+
ui_map = dict(ui) if isinstance(ui, Mapping) else {}
|
|
343
|
+
ui_map["theme"] = theme
|
|
344
|
+
ui_map.pop("textual_theme", None)
|
|
345
|
+
raw["ui"] = ui_map
|
|
346
|
+
_write_private_toml(path, raw)
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
def update_config_ui_interface(config_path: Path, interface: str) -> None:
|
|
350
|
+
if not is_valid_ui_interface(interface):
|
|
351
|
+
raise ValueError("UI interface must be one of: classic, modern.")
|
|
352
|
+
path = config_path.expanduser()
|
|
353
|
+
if path.suffix == ".json":
|
|
354
|
+
raise ValueError("Deepy only supports TOML config files; JSON config is not supported.")
|
|
355
|
+
raw = _read_toml_mapping(path)
|
|
356
|
+
ui = raw.get("ui")
|
|
357
|
+
ui_map = dict(ui) if isinstance(ui, Mapping) else {}
|
|
358
|
+
ui_map["interface"] = interface
|
|
359
|
+
raw["ui"] = ui_map
|
|
360
|
+
_write_private_toml(path, raw)
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
def update_config_ui_choice(config_path: Path, *, interface: str, theme: str) -> None:
|
|
364
|
+
if not is_valid_ui_interface(interface):
|
|
365
|
+
raise ValueError("UI interface must be one of: classic, modern.")
|
|
366
|
+
if not is_valid_ui_theme(theme):
|
|
367
|
+
raise ValueError("UI theme must be one of: dark, light.")
|
|
368
|
+
path = config_path.expanduser()
|
|
369
|
+
if path.suffix == ".json":
|
|
370
|
+
raise ValueError("Deepy only supports TOML config files; JSON config is not supported.")
|
|
371
|
+
raw = _read_toml_mapping(path)
|
|
372
|
+
ui = raw.get("ui")
|
|
373
|
+
ui_map = dict(ui) if isinstance(ui, Mapping) else {}
|
|
374
|
+
ui_map["interface"] = interface
|
|
375
|
+
ui_map["theme"] = theme
|
|
376
|
+
ui_map.pop("textual_theme", None)
|
|
377
|
+
raw["ui"] = ui_map
|
|
378
|
+
_write_private_toml(path, raw)
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
def update_config_textual_theme(config_path: Path, textual_theme: str) -> None:
|
|
382
|
+
theme = textual_theme.strip()
|
|
383
|
+
if not theme:
|
|
384
|
+
raise ValueError("Textual theme must not be empty.")
|
|
385
|
+
path = config_path.expanduser()
|
|
386
|
+
if path.suffix == ".json":
|
|
387
|
+
raise ValueError("Deepy only supports TOML config files; JSON config is not supported.")
|
|
388
|
+
raw = _read_toml_mapping(path)
|
|
389
|
+
ui = raw.get("ui")
|
|
390
|
+
ui_map = dict(ui) if isinstance(ui, Mapping) else {}
|
|
391
|
+
ui_map["textual_theme"] = theme
|
|
392
|
+
raw["ui"] = ui_map
|
|
393
|
+
_write_private_toml(path, raw)
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
def update_config_input_suggestions_enabled(config_path: Path, enabled: bool) -> None:
|
|
397
|
+
path = config_path.expanduser()
|
|
398
|
+
if path.suffix == ".json":
|
|
399
|
+
raise ValueError("Deepy only supports TOML config files; JSON config is not supported.")
|
|
400
|
+
raw = _read_toml_mapping(path)
|
|
401
|
+
ui = raw.get("ui")
|
|
402
|
+
ui_map = dict(ui) if isinstance(ui, Mapping) else {}
|
|
403
|
+
ui_map["input_suggestions_enabled"] = bool(enabled)
|
|
404
|
+
raw["ui"] = ui_map
|
|
405
|
+
_write_private_toml(path, raw)
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
def update_config_view_mode(config_path: Path, view_mode: str) -> None:
|
|
409
|
+
if not is_valid_ui_view_mode(view_mode):
|
|
410
|
+
raise ValueError("View mode must be one of: concise, full.")
|
|
411
|
+
path = config_path.expanduser()
|
|
412
|
+
if path.suffix == ".json":
|
|
413
|
+
raise ValueError("Deepy only supports TOML config files; JSON config is not supported.")
|
|
414
|
+
raw = _read_toml_mapping(path)
|
|
415
|
+
ui = raw.get("ui")
|
|
416
|
+
ui_map = dict(ui) if isinstance(ui, Mapping) else {}
|
|
417
|
+
ui_map["view_mode"] = view_mode
|
|
418
|
+
raw["ui"] = ui_map
|
|
419
|
+
_write_private_toml(path, raw)
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
def update_config_audit_mode(config_path: Path, audit_mode: str) -> None:
|
|
423
|
+
if not is_valid_config_audit_mode(audit_mode):
|
|
424
|
+
raise ValueError("Audit mode must be one of: normal, auto, yolo.")
|
|
425
|
+
path = config_path.expanduser()
|
|
426
|
+
if path.suffix == ".json":
|
|
427
|
+
raise ValueError("Deepy only supports TOML config files; JSON config is not supported.")
|
|
428
|
+
raw = _read_toml_mapping(path)
|
|
429
|
+
audit = raw.get("audit")
|
|
430
|
+
audit_map = dict(audit) if isinstance(audit, Mapping) else {}
|
|
431
|
+
audit_map["mode"] = audit_mode
|
|
432
|
+
raw["audit"] = audit_map
|
|
433
|
+
_write_private_toml(path, raw)
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
def _read_toml_mapping(path: Path) -> dict[str, Any]:
|
|
437
|
+
if not path.exists():
|
|
438
|
+
return {}
|
|
439
|
+
with path.open("rb") as fh:
|
|
440
|
+
loaded = tomllib.load(fh)
|
|
441
|
+
return dict(loaded)
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
def _write_private_toml(path: Path, raw: Mapping[str, Any]) -> None:
|
|
445
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
446
|
+
path.write_text(tomli_w.dumps(raw), encoding="utf-8")
|
|
447
|
+
os.chmod(path, 0o600)
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
def _drop_empty(value: Any) -> Any:
|
|
451
|
+
if isinstance(value, dict):
|
|
452
|
+
result = {}
|
|
453
|
+
for key, item in value.items():
|
|
454
|
+
if item is None:
|
|
455
|
+
continue
|
|
456
|
+
cleaned = _drop_empty(item)
|
|
457
|
+
if cleaned == {}:
|
|
458
|
+
continue
|
|
459
|
+
result[key] = cleaned
|
|
460
|
+
return result
|
|
461
|
+
if isinstance(value, list):
|
|
462
|
+
return [_drop_empty(item) for item in value]
|
|
463
|
+
return value
|