code-puppy 0.0.781__tar.gz → 0.0.783__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.
- {code_puppy-0.0.781 → code_puppy-0.0.783}/PKG-INFO +3 -4
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/cli_runner.py +14 -63
- code_puppy-0.0.783/code_puppy/command_line/completers.py +434 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/file_path_completion.py +1 -2
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/load_context_completion.py +1 -1
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/mcp/custom_server_form.py +15 -8
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/mcp/install_menu.py +7 -7
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/mcp_completion.py +1 -2
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/model_picker_completion.py +1 -25
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/pin_command_completion.py +1 -2
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/skills_completion.py +1 -2
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/messaging/editor_completion.py +4 -50
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/messaging/editor_history.py +9 -27
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/pydantic_patches.py +1 -62
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/tools/ask_user_question/theme.py +5 -5
- {code_puppy-0.0.781 → code_puppy-0.0.783}/pyproject.toml +3 -4
- code_puppy-0.0.781/code_puppy/command_line/prompt_toolkit_completion.py +0 -1071
- {code_puppy-0.0.781 → code_puppy-0.0.783}/.gitignore +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/LICENSE +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/README.md +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/__init__.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/__main__.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/agent_execution_context.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/agents/__init__.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/agents/_builder.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/agents/_compaction.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/agents/_diagnostics.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/agents/_history.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/agents/_key_listeners.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/agents/_model_message_transform.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/agents/_non_streaming_render.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/agents/_output_limits.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/agents/_run_signals.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/agents/_runtime.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/agents/_steer_processor.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/agents/_subagent_recursion.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/agents/agent_code_puppy.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/agents/agent_creator_agent.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/agents/agent_helios.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/agents/agent_manager.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/agents/agent_model_judge.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/agents/agent_planning.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/agents/agent_qa_kitten.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/agents/agent_web_retriever.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/agents/base_agent.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/agents/event_stream_handler.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/agents/json_agent.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/agents/retry_profiles.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/agents/run_stats.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/agents/smooth_stream.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/agents/subagent_stream_handler.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/atomic_io.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/atomic_json.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/callbacks.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/chatgpt_codex_client.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/claude_cache_client.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/__init__.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/add_model_details.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/add_model_menu.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/agent_menu.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/attachments.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/autosave_menu.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/autosave_search.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/clipboard.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/colors_menu.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/command_handler.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/command_registry.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/completion_cache.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/config_apply.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/config_commands.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/core_commands.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/file_index.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/help_catalog.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/help_overlay.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/image_utils.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/mcp/__init__.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/mcp/base.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/mcp/catalog_server_installer.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/mcp/custom_server_installer.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/mcp/edit_command.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/mcp/handler.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/mcp/help_command.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/mcp/install_command.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/mcp/list_command.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/mcp/logs_command.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/mcp/mcp_servers_store.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/mcp/remove_command.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/mcp/restart_command.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/mcp/search_command.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/mcp/silence_warning_command.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/mcp/start_all_command.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/mcp/start_command.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/mcp/status_command.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/mcp/stop_all_command.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/mcp/stop_command.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/mcp/trust_command.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/mcp/utils.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/mcp/wizard_utils.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/mcp_binding_menu.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/menu_session.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/model_settings_defs.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/model_settings_menu.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/onboarding_slides.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/onboarding_wizard.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/pagination.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/session_browser.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/session_browser_data.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/session_commands.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/set_menu.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/set_menu_catalog.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/set_menu_schema.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/set_menu_settings.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/set_menu_shims.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/set_menu_values.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/shell_passthrough.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/tui_style.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/uc_menu.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/command_line/utils.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/config.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/config_file.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/error_logging.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/gemini_code_assist.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/gemini_model.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/hook_engine/README.md +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/hook_engine/__init__.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/hook_engine/aliases.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/hook_engine/engine.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/hook_engine/executor.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/hook_engine/matcher.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/hook_engine/models.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/hook_engine/registry.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/hook_engine/validator.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/http_utils.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/i18n/__init__.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/i18n/audit.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/i18n/catalog.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/i18n/formats.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/i18n/locale.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/i18n/locales/en-US.json +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/i18n/locales/es.json +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/i18n/locales/fr-CA.json +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/i18n/plurals.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/i18n/pseudo.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/i18n/translate.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/kennel_provider.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/keymap.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/list_filtering.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/main.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/mcp_/__init__.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/mcp_/agent_bindings.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/mcp_/async_lifecycle.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/mcp_/blocking_startup.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/mcp_/circuit_breaker.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/mcp_/config_wizard.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/mcp_/dashboard.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/mcp_/error_isolation.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/mcp_/examples/retry_example.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/mcp_/health_monitor.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/mcp_/managed_server.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/mcp_/manager.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/mcp_/mcp_logs.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/mcp_/project_config.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/mcp_/registry.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/mcp_/retry_manager.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/mcp_/server_registry_catalog.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/mcp_/status_tracker.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/mcp_/system_tools.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/mcp_/tool_arg_coercion.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/mcp_/toolset_utils.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/mcp_prompts/__init__.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/mcp_prompts/hook_creator.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/messaging/__init__.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/messaging/bar_painters.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/messaging/bar_rendering.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/messaging/bottom_bar.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/messaging/bus.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/messaging/chords.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/messaging/commands.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/messaging/editor_actions.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/messaging/editor_display.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/messaging/editor_keys.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/messaging/editor_paste.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/messaging/external_editor.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/messaging/inline_bar.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/messaging/line_editor.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/messaging/markdown_patches.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/messaging/message_queue.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/messaging/messages.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/messaging/pause_controller.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/messaging/prompt_prefix_style.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/messaging/queue_console.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/messaging/renderers.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/messaging/rich_renderer.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/messaging/run_ui.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/messaging/run_ui_wiring.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/messaging/spinner/__init__.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/messaging/subagent_console.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/messaging/transcript_guard.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/model_descriptions.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/model_factory.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/model_switching.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/model_utils.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/models.json +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/models_dev_api.json +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/models_dev_parser.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/observability.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/platform_utils.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/plugins/__init__.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/plugins/config.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/plugins/oauth_pasteback.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/plugins/oauth_puppy_html.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/plugins/trust.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/plugins/trust_notice.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/provider_credentials.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/provider_identity.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/reopenable_async_client.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/round_robin_model.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/secret_store.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/secret_store_backends.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/session_format_migration.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/session_lifecycle.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/session_migration.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/session_storage.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/session_surrogate_unpickler.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/skill_provider.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/splash.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/status_display.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/terminal_utils.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/token_usage.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/tools/__init__.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/tools/agent_tools.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/tools/ask_user_question/__init__.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/tools/ask_user_question/constants.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/tools/ask_user_question/demo_tui.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/tools/ask_user_question/handler.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/tools/ask_user_question/models.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/tools/ask_user_question/registration.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/tools/ask_user_question/renderers.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/tools/ask_user_question/terminal_ui.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/tools/ask_user_question/tui_loop.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/tools/browser/__init__.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/tools/browser/browser_control.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/tools/browser/browser_interactions.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/tools/browser/browser_locator_resolver.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/tools/browser/browser_locators.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/tools/browser/browser_manager.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/tools/browser/browser_navigation.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/tools/browser/browser_page_snapshot.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/tools/browser/browser_screenshot.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/tools/browser/browser_scripts.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/tools/browser/browser_semantic_interactions.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/tools/browser/browser_workflows.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/tools/browser/tool_registry.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/tools/command_runner.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/tools/common.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/tools/display.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/tools/file_modifications.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/tools/file_operations.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/tools/file_permission_state.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/tools/fs_access.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/tools/image_tools.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/tools/io_backends.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/tools/model_tools.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/tools/shell_backgrounding.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/tools/skills_tools.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/tools/subagent_context.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/tools/subagent_invocation.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/tools/subagent_usage_metrics.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/tools/tools_content.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/tools/universal_constructor.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/undo_manager.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/universal_constructor_provider.py +0 -0
- {code_puppy-0.0.781 → code_puppy-0.0.783}/code_puppy/version_checker.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: code-puppy
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.783
|
|
4
4
|
Summary: Code generation agent
|
|
5
5
|
Project-URL: repository, https://github.com/mpfaffenberger/code_puppy
|
|
6
6
|
Project-URL: HomePage, https://github.com/mpfaffenberger/code_puppy
|
|
@@ -20,7 +20,7 @@ Requires-Dist: anthropic<1,>=0.108.0
|
|
|
20
20
|
Requires-Dist: anyio>=4.7.0
|
|
21
21
|
Requires-Dist: azure-identity>=1.15.0
|
|
22
22
|
Requires-Dist: boto3>=1.43.9
|
|
23
|
-
Requires-Dist: code-puppy-core-plugins>=0.0.
|
|
23
|
+
Requires-Dist: code-puppy-core-plugins>=0.0.26
|
|
24
24
|
Requires-Dist: httpx[http2]>=0.24.1
|
|
25
25
|
Requires-Dist: json-repair>=0.46.2
|
|
26
26
|
Requires-Dist: keyring>=24.0.0
|
|
@@ -30,7 +30,6 @@ Requires-Dist: openai>=2.45.0
|
|
|
30
30
|
Requires-Dist: packaging>=24.0
|
|
31
31
|
Requires-Dist: pillow>=10.0.0
|
|
32
32
|
Requires-Dist: playwright>=1.40.0; sys_platform != 'android'
|
|
33
|
-
Requires-Dist: prompt-toolkit>=3.0.52
|
|
34
33
|
Requires-Dist: pydantic-ai-harness==0.23.0
|
|
35
34
|
Requires-Dist: pydantic-ai-slim[anthropic,mcp,openai]==2.31.0
|
|
36
35
|
Requires-Dist: pydantic>=2.4.0
|
|
@@ -40,7 +39,7 @@ Requires-Dist: rapidfuzz>=3.13.0
|
|
|
40
39
|
Requires-Dist: requests>=2.28.0
|
|
41
40
|
Requires-Dist: rich>=13.4.2
|
|
42
41
|
Requires-Dist: ripgrep==14.1.0; sys_platform != 'android'
|
|
43
|
-
Requires-Dist: termflow-md>=0.
|
|
42
|
+
Requires-Dist: termflow-md>=0.9.0
|
|
44
43
|
Requires-Dist: typer>=0.12.0
|
|
45
44
|
Provides-Extra: bedrock
|
|
46
45
|
Requires-Dist: boto3>=1.35.0; extra == 'bedrock'
|
|
@@ -371,7 +371,7 @@ async def main():
|
|
|
371
371
|
)
|
|
372
372
|
|
|
373
373
|
if disable_windows_ctrl_c():
|
|
374
|
-
# Keep the clamp sticky across terminal resets /
|
|
374
|
+
# Keep the clamp sticky across terminal resets / console mode restores.
|
|
375
375
|
set_keep_ctrl_c_disabled(True)
|
|
376
376
|
|
|
377
377
|
# Load API keys from puppy.cfg into environment variables
|
|
@@ -618,7 +618,7 @@ async def main():
|
|
|
618
618
|
def _use_persistent_prompt() -> bool:
|
|
619
619
|
"""Should the REPL use the persistent bottom-bar prompt (Phase A)?
|
|
620
620
|
|
|
621
|
-
False (
|
|
621
|
+
False (-> classic plain-input path) when:
|
|
622
622
|
* rollback flag: env CODE_PUPPY_CLASSIC_PROMPT=1 or config
|
|
623
623
|
``classic_prompt`` truthy — protects the eyeball-testing period;
|
|
624
624
|
* CODE_PUPPY_NO_TUI=1 (tests / pexpect harnesses);
|
|
@@ -666,7 +666,7 @@ def _persistent_prompt_parts() -> tuple:
|
|
|
666
666
|
in-band escapes, so colors can't ride inside the string itself).
|
|
667
667
|
"""
|
|
668
668
|
try:
|
|
669
|
-
from code_puppy.command_line.
|
|
669
|
+
from code_puppy.command_line.completers import (
|
|
670
670
|
PROMPT_STYLES,
|
|
671
671
|
get_prompt_with_active_model,
|
|
672
672
|
)
|
|
@@ -820,35 +820,6 @@ async def interactive_mode(message_renderer, initial_command: str = None) -> Non
|
|
|
820
820
|
|
|
821
821
|
emit_error(t("cli.initial_command.error", error=str(e)))
|
|
822
822
|
|
|
823
|
-
# Check if prompt_toolkit is installed
|
|
824
|
-
try:
|
|
825
|
-
from code_puppy.command_line.prompt_toolkit_completion import (
|
|
826
|
-
get_input_with_combined_completion,
|
|
827
|
-
get_prompt_with_active_model,
|
|
828
|
-
)
|
|
829
|
-
except ImportError:
|
|
830
|
-
from code_puppy.messaging import emit_warning
|
|
831
|
-
|
|
832
|
-
emit_warning(t("cli.prompt_toolkit.installing"))
|
|
833
|
-
try:
|
|
834
|
-
import subprocess
|
|
835
|
-
|
|
836
|
-
subprocess.check_call(
|
|
837
|
-
[sys.executable, "-m", "pip", "install", "--quiet", "prompt_toolkit"]
|
|
838
|
-
)
|
|
839
|
-
from code_puppy.messaging import emit_success
|
|
840
|
-
|
|
841
|
-
emit_success(t("cli.prompt_toolkit.installed"))
|
|
842
|
-
from code_puppy.command_line.prompt_toolkit_completion import (
|
|
843
|
-
get_input_with_combined_completion,
|
|
844
|
-
get_prompt_with_active_model,
|
|
845
|
-
)
|
|
846
|
-
except Exception as e:
|
|
847
|
-
from code_puppy.messaging import emit_error, emit_warning
|
|
848
|
-
|
|
849
|
-
emit_error(t("cli.prompt_toolkit.install_error", error=e))
|
|
850
|
-
emit_warning(t("cli.prompt_toolkit.fallback"))
|
|
851
|
-
|
|
852
823
|
# Autosave loading is now manual - use /autosave_load command
|
|
853
824
|
|
|
854
825
|
record_terminal_session(get_current_session_name(), overwrite=False)
|
|
@@ -926,47 +897,27 @@ async def interactive_mode(message_renderer, initial_command: str = None) -> Non
|
|
|
926
897
|
# doubled every line's noise. Text() so bracket-y input renders as-is.
|
|
927
898
|
emit_info(_prompt_echo_text(task))
|
|
928
899
|
else:
|
|
929
|
-
#
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
get_prompt_with_active_model(),
|
|
937
|
-
history_file=COMMAND_HISTORY_FILE,
|
|
938
|
-
)
|
|
939
|
-
|
|
940
|
-
# Windows: re-clamp raw-Ctrl+C mode after prompt_toolkit
|
|
941
|
-
# (prompt_toolkit restores console mode on exit)
|
|
942
|
-
try:
|
|
943
|
-
from code_puppy.terminal_utils import ensure_ctrl_c_disabled
|
|
900
|
+
# Classic (non-TTY / rollback) prompt: plain blocking input.
|
|
901
|
+
# No completion here -- the persistent bottom-bar editor is
|
|
902
|
+
# the featured path; this branch exists for pipes, CI, and
|
|
903
|
+
# the CODE_PUPPY_CLASSIC_PROMPT escape hatch.
|
|
904
|
+
reset_windows_terminal_ansi()
|
|
905
|
+
task = input(">>> ")
|
|
906
|
+
from code_puppy.messaging.editor_history import HistoryStore
|
|
944
907
|
|
|
945
|
-
|
|
946
|
-
except ImportError:
|
|
947
|
-
pass
|
|
948
|
-
except ImportError:
|
|
949
|
-
# Fall back to basic input if prompt_toolkit is not available
|
|
950
|
-
task = input(">>> ")
|
|
908
|
+
HistoryStore(COMMAND_HISTORY_FILE).append(task)
|
|
951
909
|
|
|
952
910
|
except (KeyboardInterrupt, asyncio.CancelledError) as cancel_exc:
|
|
953
911
|
# Ctrl+C: cancel input and continue. Reset terminal state on Windows
|
|
954
912
|
# so it doesn't become unresponsive.
|
|
955
913
|
reset_windows_terminal_full()
|
|
956
914
|
from code_puppy.callbacks import on_interactive_turn_cancel
|
|
957
|
-
from code_puppy.command_line.prompt_toolkit_completion import (
|
|
958
|
-
pop_non_ctrl_c_cancel,
|
|
959
|
-
)
|
|
960
915
|
from code_puppy.messaging import emit_success, emit_warning
|
|
961
916
|
from code_puppy.messaging.run_ui import DOUBLE_CTRL_C_WINDOW_S
|
|
962
917
|
|
|
963
|
-
# Double Ctrl+C at the idle prompt quits like Ctrl+D.
|
|
964
|
-
#
|
|
965
|
-
|
|
966
|
-
was_plain_ctrl_c = (
|
|
967
|
-
isinstance(cancel_exc, KeyboardInterrupt)
|
|
968
|
-
and not pop_non_ctrl_c_cancel()
|
|
969
|
-
)
|
|
918
|
+
# Double Ctrl+C at the idle prompt quits like Ctrl+D.
|
|
919
|
+
# CancelledError never counts.
|
|
920
|
+
was_plain_ctrl_c = isinstance(cancel_exc, KeyboardInterrupt)
|
|
970
921
|
now = time.monotonic()
|
|
971
922
|
if was_plain_ctrl_c and now - last_idle_ctrl_c <= DOUBLE_CTRL_C_WINDOW_S:
|
|
972
923
|
emit_success("\n" + t("cli.goodbye_ctrld"))
|
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
"""Slash-command completers and the REPL prompt prefix.
|
|
2
|
+
|
|
3
|
+
Home of the completion stack (on termflow's completer protocol) and the
|
|
4
|
+
prompt-prefix builder, extracted from the retired classic
|
|
5
|
+
prompt_toolkit input path. The raw bottom-bar editor consumes the stack
|
|
6
|
+
through :func:`build_completer_stack`.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import os
|
|
12
|
+
import unicodedata
|
|
13
|
+
|
|
14
|
+
from termflow.tui.completion import Completer, Completion, merge_completers
|
|
15
|
+
|
|
16
|
+
from code_puppy.command_line.command_registry import get_unique_commands
|
|
17
|
+
from code_puppy.command_line.completion_cache import TTLCache
|
|
18
|
+
from code_puppy.command_line.utils import list_directory
|
|
19
|
+
from code_puppy.config import (
|
|
20
|
+
get_config_keys,
|
|
21
|
+
get_puppy_name,
|
|
22
|
+
get_value,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
_config_keys_cache: TTLCache[tuple[object, list[str]]] = TTLCache()
|
|
26
|
+
_slash_catalog_cache: TTLCache[list[dict[str, str]]] = TTLCache()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _sanitize_for_encoding(text: str) -> str:
|
|
30
|
+
"""Remove or replace characters that can't be safely encoded.
|
|
31
|
+
|
|
32
|
+
Handles lone surrogates (U+D800-U+DFFF, invalid in UTF-8) and other
|
|
33
|
+
problematic sequences from Windows copy-paste.
|
|
34
|
+
"""
|
|
35
|
+
try:
|
|
36
|
+
text.encode("utf-8")
|
|
37
|
+
return text # String is already valid UTF-8
|
|
38
|
+
except UnicodeEncodeError:
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
try:
|
|
42
|
+
return text.encode("utf-8", errors="surrogatepass").decode(
|
|
43
|
+
"utf-8", errors="replace"
|
|
44
|
+
)
|
|
45
|
+
except (UnicodeEncodeError, UnicodeDecodeError):
|
|
46
|
+
# Last resort: filter out all non-BMP and surrogate characters
|
|
47
|
+
return "".join(
|
|
48
|
+
char
|
|
49
|
+
for char in text
|
|
50
|
+
if ord(char) < 0xD800 or (ord(char) > 0xDFFF and ord(char) < 0x10000)
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _strip_variation_selectors(text: str) -> str:
|
|
55
|
+
"""Remove variation selectors (U+FE00-FE0F) from text.
|
|
56
|
+
|
|
57
|
+
These invisible characters modify emoji rendering but cause width
|
|
58
|
+
calculation mismatches between line editors and terminal emulators.
|
|
59
|
+
"""
|
|
60
|
+
return "".join(c for c in text if not (0xFE00 <= ord(c) <= 0xFE0F))
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _normalize_emoji_spacing(text: str) -> str:
|
|
64
|
+
"""Normalize emoji spacing for consistent terminal rendering.
|
|
65
|
+
|
|
66
|
+
Some emojis have East Asian Width 'N' (Neutral) which terminals render
|
|
67
|
+
inconsistently. This adds a space after such emojis to prevent
|
|
68
|
+
the following character from overlapping.
|
|
69
|
+
"""
|
|
70
|
+
result = []
|
|
71
|
+
text = _strip_variation_selectors(text)
|
|
72
|
+
for char in text:
|
|
73
|
+
result.append(char)
|
|
74
|
+
# Add padding after Neutral-width emoji to prevent overlap
|
|
75
|
+
if (
|
|
76
|
+
0x1F300 <= ord(char) <= 0x1FAFF
|
|
77
|
+
and unicodedata.east_asian_width(char) == "N"
|
|
78
|
+
):
|
|
79
|
+
result.append(" ") # Extra space buffer
|
|
80
|
+
return "".join(result)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
# ---------------------------------------------------------------------------
|
|
84
|
+
# Completers
|
|
85
|
+
# ---------------------------------------------------------------------------
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class SetCompleter(Completer):
|
|
89
|
+
def __init__(self, trigger: str = "/set"):
|
|
90
|
+
self.trigger = trigger
|
|
91
|
+
|
|
92
|
+
def get_completions(self, document, complete_event):
|
|
93
|
+
cursor_position = document.cursor_position
|
|
94
|
+
text_before_cursor = document.text_before_cursor
|
|
95
|
+
stripped_text_for_trigger_check = text_before_cursor.lstrip()
|
|
96
|
+
|
|
97
|
+
# If user types just /set (no space), suggest adding a space
|
|
98
|
+
if stripped_text_for_trigger_check == self.trigger:
|
|
99
|
+
yield Completion(
|
|
100
|
+
self.trigger + " ",
|
|
101
|
+
start_position=-len(self.trigger),
|
|
102
|
+
display=self.trigger + " ",
|
|
103
|
+
display_meta="set config key",
|
|
104
|
+
)
|
|
105
|
+
return
|
|
106
|
+
|
|
107
|
+
# Require a space after /set before showing completions
|
|
108
|
+
if not stripped_text_for_trigger_check.startswith(self.trigger + " "):
|
|
109
|
+
return
|
|
110
|
+
|
|
111
|
+
# Determine the part of the text that is relevant for this completer
|
|
112
|
+
# (handles cases like " /set foo" where the trigger isn't at index 0)
|
|
113
|
+
actual_trigger_pos = text_before_cursor.find(self.trigger)
|
|
114
|
+
|
|
115
|
+
# Extract the input after /set and space (up to cursor)
|
|
116
|
+
trigger_end = actual_trigger_pos + len(self.trigger) + 1 # +1 for the space
|
|
117
|
+
text_after_trigger = text_before_cursor[trigger_end:cursor_position].lstrip()
|
|
118
|
+
start_position = -len(text_after_trigger)
|
|
119
|
+
|
|
120
|
+
# --- SPECIAL HANDLING FOR 'model' KEY ---
|
|
121
|
+
if text_after_trigger == "model":
|
|
122
|
+
# Don't return any completions -- let ModelNameCompleter handle it
|
|
123
|
+
return
|
|
124
|
+
|
|
125
|
+
# Get config keys and sort them alphabetically for consistent display.
|
|
126
|
+
# Per-model controls belong exclusively to /model_settings.
|
|
127
|
+
from code_puppy.command_line.config_apply import MODEL_SETTINGS_ONLY_KEYS
|
|
128
|
+
|
|
129
|
+
cached_getter, config_keys = _config_keys_cache.get(
|
|
130
|
+
lambda: (get_config_keys, sorted(get_config_keys()))
|
|
131
|
+
)
|
|
132
|
+
if cached_getter is not get_config_keys:
|
|
133
|
+
_config_keys_cache.clear()
|
|
134
|
+
_, config_keys = _config_keys_cache.get(
|
|
135
|
+
lambda: (get_config_keys, sorted(get_config_keys()))
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
for key in config_keys:
|
|
139
|
+
if key in {"model", "puppy_token"} | MODEL_SETTINGS_ONLY_KEYS:
|
|
140
|
+
continue
|
|
141
|
+
if key.startswith(text_after_trigger):
|
|
142
|
+
prev_value = get_value(key)
|
|
143
|
+
value_part = f" = {prev_value}" if prev_value is not None else " = "
|
|
144
|
+
completion_text = f"{key}{value_part}"
|
|
145
|
+
|
|
146
|
+
yield Completion(
|
|
147
|
+
completion_text,
|
|
148
|
+
start_position=start_position,
|
|
149
|
+
display_meta="",
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class CDCompleter(Completer):
|
|
154
|
+
def __init__(self, trigger: str = "/cd"):
|
|
155
|
+
self.trigger = trigger
|
|
156
|
+
|
|
157
|
+
def get_completions(self, document, complete_event):
|
|
158
|
+
text_before_cursor = document.text_before_cursor
|
|
159
|
+
stripped_text = text_before_cursor.lstrip()
|
|
160
|
+
|
|
161
|
+
# Require a space after /cd before showing completions
|
|
162
|
+
if not stripped_text.startswith(self.trigger + " "):
|
|
163
|
+
return
|
|
164
|
+
|
|
165
|
+
# Extract the directory path after /cd and space (up to cursor)
|
|
166
|
+
trigger_pos = text_before_cursor.find(self.trigger)
|
|
167
|
+
trigger_end = trigger_pos + len(self.trigger) + 1 # +1 for the space
|
|
168
|
+
dir_path = text_before_cursor[trigger_end:].lstrip()
|
|
169
|
+
start_position = -(len(dir_path))
|
|
170
|
+
|
|
171
|
+
try:
|
|
172
|
+
# Treat a bare `~` as `~/` so we complete inside the home
|
|
173
|
+
# directory, not the parent containing the username folder.
|
|
174
|
+
lookup_path = "~/" if dir_path == "~" else dir_path
|
|
175
|
+
expanded_lookup = os.path.expanduser(lookup_path)
|
|
176
|
+
|
|
177
|
+
# If the typed path ends with a separator, we're completing inside
|
|
178
|
+
# that directory and should match all child names.
|
|
179
|
+
if lookup_path.endswith(os.sep):
|
|
180
|
+
part = expanded_lookup
|
|
181
|
+
name_prefix = ""
|
|
182
|
+
else:
|
|
183
|
+
part = os.path.dirname(expanded_lookup) or "."
|
|
184
|
+
name_prefix = os.path.basename(expanded_lookup)
|
|
185
|
+
|
|
186
|
+
dirs, _ = list_directory(part)
|
|
187
|
+
dirnames = [d for d in dirs if d.startswith(name_prefix)]
|
|
188
|
+
|
|
189
|
+
# Preserve user's typed style (~, relative, absolute) in emitted
|
|
190
|
+
# completion text instead of leaking expanded absolute paths.
|
|
191
|
+
if dir_path == "~":
|
|
192
|
+
typed_base = "~"
|
|
193
|
+
elif dir_path.endswith(os.sep):
|
|
194
|
+
stripped_base = dir_path.rstrip(os.sep)
|
|
195
|
+
if not stripped_base and dir_path.startswith(os.sep):
|
|
196
|
+
typed_base = os.sep
|
|
197
|
+
else:
|
|
198
|
+
typed_base = stripped_base
|
|
199
|
+
else:
|
|
200
|
+
typed_base = os.path.dirname(dir_path.rstrip(os.sep))
|
|
201
|
+
|
|
202
|
+
for d in dirnames:
|
|
203
|
+
suggestion = os.path.join(typed_base, d) if typed_base else d
|
|
204
|
+
suggestion = suggestion.rstrip(os.sep) + os.sep
|
|
205
|
+
yield Completion(
|
|
206
|
+
suggestion,
|
|
207
|
+
start_position=start_position,
|
|
208
|
+
display=d + os.sep,
|
|
209
|
+
display_meta="Directory",
|
|
210
|
+
)
|
|
211
|
+
except Exception:
|
|
212
|
+
# Silently ignore errors (e.g., permission issues, non-existent dir)
|
|
213
|
+
pass
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
class AgentCompleter(Completer):
|
|
217
|
+
"""A completer that triggers on '/agent' to show available agents."""
|
|
218
|
+
|
|
219
|
+
def __init__(self, trigger: str = "/agent", prefix: str = ""):
|
|
220
|
+
self.trigger = trigger
|
|
221
|
+
self.prefix = prefix
|
|
222
|
+
|
|
223
|
+
def get_completions(self, document, complete_event):
|
|
224
|
+
cursor_position = document.cursor_position
|
|
225
|
+
text_before_cursor = document.text_before_cursor
|
|
226
|
+
stripped_text = text_before_cursor.lstrip()
|
|
227
|
+
|
|
228
|
+
# Require a space after /agent before showing completions
|
|
229
|
+
if not stripped_text.startswith(self.trigger + " "):
|
|
230
|
+
return
|
|
231
|
+
|
|
232
|
+
# Extract the input after /agent and space (up to cursor)
|
|
233
|
+
trigger_pos = text_before_cursor.find(self.trigger)
|
|
234
|
+
trigger_end = trigger_pos + len(self.trigger) + 1 # +1 for the space
|
|
235
|
+
text_after_trigger = text_before_cursor[trigger_end:cursor_position].lstrip()
|
|
236
|
+
if self.prefix:
|
|
237
|
+
if not text_after_trigger.startswith(self.prefix):
|
|
238
|
+
return
|
|
239
|
+
text_after_trigger = text_after_trigger[len(self.prefix) :]
|
|
240
|
+
start_position = -len(text_after_trigger)
|
|
241
|
+
|
|
242
|
+
try:
|
|
243
|
+
from code_puppy.command_line.pin_command_completion import load_agent_names
|
|
244
|
+
|
|
245
|
+
agent_names = load_agent_names()
|
|
246
|
+
except Exception:
|
|
247
|
+
return
|
|
248
|
+
|
|
249
|
+
try:
|
|
250
|
+
from code_puppy.command_line.pin_command_completion import (
|
|
251
|
+
_get_agent_display_meta,
|
|
252
|
+
)
|
|
253
|
+
except ImportError:
|
|
254
|
+
_get_agent_display_meta = lambda x: "default" # noqa: E731
|
|
255
|
+
|
|
256
|
+
for agent_name in agent_names:
|
|
257
|
+
if agent_name.lower().startswith(text_after_trigger.lower()):
|
|
258
|
+
yield Completion(
|
|
259
|
+
agent_name,
|
|
260
|
+
start_position=start_position,
|
|
261
|
+
display=agent_name,
|
|
262
|
+
display_meta=_get_agent_display_meta(agent_name),
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def _read_slash_catalog() -> list[dict[str, str]]:
|
|
267
|
+
"""Load registered and plugin command help without filtering or reordering."""
|
|
268
|
+
catalog = []
|
|
269
|
+
for cmd in get_unique_commands():
|
|
270
|
+
catalog.append(
|
|
271
|
+
{"text": cmd.name, "display": f"/{cmd.name}", "meta": cmd.description}
|
|
272
|
+
)
|
|
273
|
+
catalog.extend(
|
|
274
|
+
{
|
|
275
|
+
"text": alias,
|
|
276
|
+
"display": f"/{alias} (alias for /{cmd.name})",
|
|
277
|
+
"meta": cmd.description,
|
|
278
|
+
}
|
|
279
|
+
for alias in cmd.aliases
|
|
280
|
+
)
|
|
281
|
+
try:
|
|
282
|
+
from code_puppy import callbacks, plugins
|
|
283
|
+
|
|
284
|
+
plugins.load_plugin_callbacks()
|
|
285
|
+
for result in callbacks.on_custom_command_help():
|
|
286
|
+
items = result if isinstance(result, list) else [result]
|
|
287
|
+
for item in items:
|
|
288
|
+
if isinstance(item, tuple) and len(item) == 2:
|
|
289
|
+
name, description = map(str, item)
|
|
290
|
+
catalog.append(
|
|
291
|
+
{"text": name, "display": f"/{name}", "meta": description}
|
|
292
|
+
)
|
|
293
|
+
except Exception:
|
|
294
|
+
pass
|
|
295
|
+
return catalog
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
class SlashCompleter(Completer):
|
|
299
|
+
"""Completes '/' at the beginning of the line with all slash commands."""
|
|
300
|
+
|
|
301
|
+
def get_completions(self, document, complete_event):
|
|
302
|
+
text_before_cursor = document.text_before_cursor
|
|
303
|
+
stripped_text = text_before_cursor.lstrip()
|
|
304
|
+
|
|
305
|
+
# Only trigger if '/' is the first non-whitespace character
|
|
306
|
+
if not stripped_text.startswith("/"):
|
|
307
|
+
return
|
|
308
|
+
|
|
309
|
+
# A bare slash intentionally yields every command so the menu
|
|
310
|
+
# appears immediately while typing.
|
|
311
|
+
partial = stripped_text[1:]
|
|
312
|
+
start_position = -len(partial)
|
|
313
|
+
|
|
314
|
+
try:
|
|
315
|
+
catalog = _slash_catalog_cache.get(_read_slash_catalog)
|
|
316
|
+
except Exception:
|
|
317
|
+
return
|
|
318
|
+
partial_lower = partial.lower()
|
|
319
|
+
all_completions = [
|
|
320
|
+
item for item in catalog if item["text"].lower().startswith(partial_lower)
|
|
321
|
+
]
|
|
322
|
+
all_completions.sort(key=lambda item: item["text"].lower())
|
|
323
|
+
|
|
324
|
+
# Strip variation selectors (U+FE00-FE0F) from display strings: they
|
|
325
|
+
# cause width mismatches -> phantom spaces (e.g. in the /judges menu).
|
|
326
|
+
for completion in all_completions:
|
|
327
|
+
yield Completion(
|
|
328
|
+
completion["text"],
|
|
329
|
+
start_position=start_position,
|
|
330
|
+
display=_strip_variation_selectors(completion["display"]),
|
|
331
|
+
display_meta=_strip_variation_selectors(completion["meta"]),
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
def build_completer_stack() -> Completer:
|
|
336
|
+
"""The full REPL completer stack (single source of truth)."""
|
|
337
|
+
from code_puppy.callbacks import get_completion_providers
|
|
338
|
+
from code_puppy.command_line.file_path_completion import FilePathCompleter
|
|
339
|
+
from code_puppy.command_line.load_context_completion import LoadContextCompleter
|
|
340
|
+
from code_puppy.command_line.mcp_completion import MCPCompleter
|
|
341
|
+
from code_puppy.command_line.model_picker_completion import ModelNameCompleter
|
|
342
|
+
from code_puppy.command_line.pin_command_completion import (
|
|
343
|
+
PinCompleter,
|
|
344
|
+
UnpinCompleter,
|
|
345
|
+
)
|
|
346
|
+
from code_puppy.command_line.skills_completion import SkillsCompleter
|
|
347
|
+
|
|
348
|
+
return merge_completers(
|
|
349
|
+
[
|
|
350
|
+
FilePathCompleter(symbol="@"),
|
|
351
|
+
ModelNameCompleter(trigger="/model"),
|
|
352
|
+
ModelNameCompleter(trigger="/m"),
|
|
353
|
+
CDCompleter(trigger="/cd"),
|
|
354
|
+
SetCompleter(trigger="/set"),
|
|
355
|
+
LoadContextCompleter(trigger="/load_context"),
|
|
356
|
+
PinCompleter(trigger="/pin_model"),
|
|
357
|
+
UnpinCompleter(trigger="/unpin"),
|
|
358
|
+
AgentCompleter(trigger="/agent"),
|
|
359
|
+
AgentCompleter(trigger="/a"),
|
|
360
|
+
AgentCompleter(trigger="/switch-agent"),
|
|
361
|
+
AgentCompleter(trigger="/sa"),
|
|
362
|
+
AgentCompleter(trigger="/fork", prefix="@"),
|
|
363
|
+
ModelNameCompleter(trigger="/fork", prefix="@"),
|
|
364
|
+
MCPCompleter(trigger="/mcp"),
|
|
365
|
+
SkillsCompleter(trigger="/skills"),
|
|
366
|
+
*get_completion_providers(),
|
|
367
|
+
SlashCompleter(),
|
|
368
|
+
]
|
|
369
|
+
)
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
# ---------------------------------------------------------------------------
|
|
373
|
+
# Prompt prefix
|
|
374
|
+
# ---------------------------------------------------------------------------
|
|
375
|
+
|
|
376
|
+
# REPL prompt palette (source of truth -- the bottom-bar prompt converts
|
|
377
|
+
# these to raw SGR via messaging.prompt_prefix_style). IMPORTANT: use
|
|
378
|
+
# `ansi*` names -- bare names resolve to truecolor hex and IGNORE the terminal
|
|
379
|
+
# palette, so /theme's OSC remap couldn't restyle the prompt.
|
|
380
|
+
PROMPT_STYLES = {
|
|
381
|
+
"puppy": "bold ansimagenta",
|
|
382
|
+
"agent": "bold ansiblue",
|
|
383
|
+
"model": "bold ansicyan",
|
|
384
|
+
"cwd": "bold ansigreen",
|
|
385
|
+
"arrow": "bold ansiyellow",
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
def get_prompt_with_active_model(base: str = ">>> ") -> list:
|
|
390
|
+
"""Styled prompt fragments: puppy [agent] [model] (cwd) >>>.
|
|
391
|
+
|
|
392
|
+
Returns a plain list of ``(style, text)`` tuples (the FormattedText
|
|
393
|
+
shape without the class) for ``flatten_prompt_fragments``.
|
|
394
|
+
"""
|
|
395
|
+
from code_puppy.agents.agent_manager import get_current_agent
|
|
396
|
+
from code_puppy.command_line.model_picker_completion import get_active_model
|
|
397
|
+
|
|
398
|
+
puppy = get_puppy_name()
|
|
399
|
+
# When nothing is configured this is None - surface that explicitly as
|
|
400
|
+
# [None] so the user immediately sees they need to /add_model.
|
|
401
|
+
global_model = get_active_model()
|
|
402
|
+
|
|
403
|
+
current_agent = get_current_agent()
|
|
404
|
+
agent_display = current_agent.display_name if current_agent else "code-puppy"
|
|
405
|
+
|
|
406
|
+
agent_model = None
|
|
407
|
+
if current_agent and hasattr(current_agent, "get_model_name"):
|
|
408
|
+
agent_model = current_agent.get_model_name()
|
|
409
|
+
|
|
410
|
+
if agent_model and agent_model != global_model:
|
|
411
|
+
model_display = f"[{global_model} \u2192 {agent_model}]"
|
|
412
|
+
elif agent_model:
|
|
413
|
+
model_display = f"[{agent_model}]"
|
|
414
|
+
else:
|
|
415
|
+
# global_model may be None -> renders as [None].
|
|
416
|
+
model_display = f"[{global_model}]"
|
|
417
|
+
|
|
418
|
+
cwd = os.getcwd()
|
|
419
|
+
home = os.path.expanduser("~")
|
|
420
|
+
if cwd.startswith(home):
|
|
421
|
+
cwd_display = "~" + cwd[len(home) :]
|
|
422
|
+
else:
|
|
423
|
+
cwd_display = cwd
|
|
424
|
+
return [
|
|
425
|
+
("class:puppy class:tui.header", f"{puppy}"),
|
|
426
|
+
("", " "),
|
|
427
|
+
(
|
|
428
|
+
"class:agent class:tui.label",
|
|
429
|
+
f"[{_normalize_emoji_spacing(agent_display)}] ",
|
|
430
|
+
),
|
|
431
|
+
("class:model class:tui.title", model_display + " "),
|
|
432
|
+
("class:cwd class:tui.muted", "(" + str(cwd_display) + ") "),
|
|
433
|
+
("class:arrow class:tui.help-key", str(base)),
|
|
434
|
+
]
|
|
@@ -20,8 +20,7 @@ import glob
|
|
|
20
20
|
import os
|
|
21
21
|
from typing import Iterable, List, Tuple
|
|
22
22
|
|
|
23
|
-
from
|
|
24
|
-
from prompt_toolkit.document import Document
|
|
23
|
+
from termflow.tui.completion import Completer, Completion, Document
|
|
25
24
|
|
|
26
25
|
from code_puppy.command_line import file_index
|
|
27
26
|
|
|
@@ -294,14 +294,10 @@ def build_form_menu(form: CustomServerForm, initial_index: int = 0, **overrides)
|
|
|
294
294
|
|
|
295
295
|
json_status = "valid" if form._validate_json() else "INVALID"
|
|
296
296
|
items = [
|
|
297
|
-
MenuItem(
|
|
298
|
-
f"Server Name: {form.server_name or '(not set)'}", value=_FIELD_NAME
|
|
299
|
-
),
|
|
297
|
+
MenuItem(f"Server Name: {form.server_name or '(not set)'}", value=_FIELD_NAME),
|
|
300
298
|
MenuItem(f"Server Type: {form._get_current_type()}", value=_FIELD_TYPE),
|
|
301
299
|
MenuItem(f"JSON Configuration ({json_status})", value=_FIELD_JSON),
|
|
302
|
-
MenuItem(
|
|
303
|
-
f"Load example for {form._get_current_type()}", value=_ACTION_EXAMPLE
|
|
304
|
-
),
|
|
300
|
+
MenuItem(f"Load example for {form._get_current_type()}", value=_ACTION_EXAMPLE),
|
|
305
301
|
MenuItem("Save & Install", value=_ACTION_SAVE),
|
|
306
302
|
MenuItem("Cancel", value=_ACTION_CANCEL),
|
|
307
303
|
]
|
|
@@ -414,7 +410,11 @@ def run_json_fallback_editor(form: CustomServerForm, **overrides) -> None:
|
|
|
414
410
|
return f"Invalid JSON: {e.msg}"
|
|
415
411
|
return None
|
|
416
412
|
|
|
417
|
-
compact =
|
|
413
|
+
compact = (
|
|
414
|
+
json.dumps(json.loads(form.json_config))
|
|
415
|
+
if _parses(form.json_config)
|
|
416
|
+
else form.json_config
|
|
417
|
+
)
|
|
418
418
|
builder = (
|
|
419
419
|
TextInputBuilder("JSON Configuration (single line)")
|
|
420
420
|
.prompt("JSON: ")
|
|
@@ -456,7 +456,14 @@ def run_form_flow(
|
|
|
456
456
|
) -> bool:
|
|
457
457
|
"""The field-edit loop. True when a server was installed."""
|
|
458
458
|
cursor = 0
|
|
459
|
-
field_order = [
|
|
459
|
+
field_order = [
|
|
460
|
+
_FIELD_NAME,
|
|
461
|
+
_FIELD_TYPE,
|
|
462
|
+
_FIELD_JSON,
|
|
463
|
+
_ACTION_EXAMPLE,
|
|
464
|
+
_ACTION_SAVE,
|
|
465
|
+
_ACTION_CANCEL,
|
|
466
|
+
]
|
|
460
467
|
while True:
|
|
461
468
|
result = menu_factory(form, initial_index=cursor).run()
|
|
462
469
|
if result.cancelled or result.item is None:
|