code-puppy 0.0.575__tar.gz → 0.0.577__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.575 → code_puppy-0.0.577}/PKG-INFO +1 -1
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/cli_runner.py +58 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/autosave_menu.py +222 -15
- code_puppy-0.0.577/code_puppy/command_line/autosave_search.py +184 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/command_registry.py +12 -2
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/core_commands.py +55 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/session_commands.py +103 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/config.py +289 -0
- code_puppy-0.0.577/code_puppy/plugins/quick_resume/__init__.py +0 -0
- code_puppy-0.0.577/code_puppy/plugins/quick_resume/register_callbacks.py +79 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/statusline/payload.py +7 -2
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/tools/file_modifications.py +6 -0
- code_puppy-0.0.577/code_puppy/undo_manager.py +62 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/pyproject.toml +1 -1
- {code_puppy-0.0.575 → code_puppy-0.0.577}/.gitignore +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/LICENSE +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/README.md +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/__main__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/agents/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/agents/_builder.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/agents/_compaction.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/agents/_diagnostics.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/agents/_history.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/agents/_key_listeners.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/agents/_non_streaming_render.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/agents/_run_signals.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/agents/_runtime.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/agents/_steer_processor.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/agents/agent_code_puppy.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/agents/agent_creator_agent.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/agents/agent_helios.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/agents/agent_manager.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/agents/agent_planning.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/agents/agent_qa_kitten.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/agents/base_agent.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/agents/event_stream_handler.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/agents/json_agent.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/agents/run_stats.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/agents/smooth_stream.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/agents/subagent_stream_handler.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/callbacks.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/chatgpt_codex_client.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/claude_cache_client.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/add_model_menu.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/agent_menu.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/attachments.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/clipboard.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/colors_menu.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/command_handler.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/config_apply.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/config_commands.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/diff_menu.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/file_index.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/file_path_completion.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/judges_menu.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/load_context_completion.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/mcp/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/mcp/base.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/mcp/catalog_server_installer.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/mcp/custom_server_form.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/mcp/custom_server_installer.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/mcp/edit_command.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/mcp/handler.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/mcp/help_command.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/mcp/install_command.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/mcp/install_menu.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/mcp/list_command.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/mcp/logs_command.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/mcp/remove_command.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/mcp/restart_command.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/mcp/search_command.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/mcp/silence_warning_command.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/mcp/start_all_command.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/mcp/start_command.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/mcp/status_command.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/mcp/stop_all_command.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/mcp/stop_command.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/mcp/utils.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/mcp/wizard_utils.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/mcp_binding_menu.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/mcp_completion.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/model_picker_completion.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/model_settings_menu.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/onboarding_slides.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/onboarding_wizard.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/pagination.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/pin_command_completion.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/prompt_toolkit_completion.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/set_menu.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/set_menu_catalog.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/set_menu_render.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/set_menu_schema.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/set_menu_settings.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/set_menu_shims.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/set_menu_values.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/shell_passthrough.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/skills_completion.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/uc_menu.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/utils.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/command_line/wiggum_state.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/error_logging.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/gemini_code_assist.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/gemini_model.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/hook_engine/README.md +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/hook_engine/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/hook_engine/aliases.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/hook_engine/engine.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/hook_engine/executor.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/hook_engine/matcher.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/hook_engine/models.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/hook_engine/registry.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/hook_engine/validator.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/http_utils.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/keymap.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/list_filtering.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/main.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/mcp_/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/mcp_/agent_bindings.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/mcp_/async_lifecycle.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/mcp_/blocking_startup.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/mcp_/captured_stdio_server.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/mcp_/circuit_breaker.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/mcp_/config_wizard.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/mcp_/dashboard.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/mcp_/error_isolation.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/mcp_/examples/retry_example.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/mcp_/health_monitor.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/mcp_/managed_server.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/mcp_/manager.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/mcp_/mcp_logs.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/mcp_/registry.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/mcp_/retry_manager.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/mcp_/server_registry_catalog.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/mcp_/status_tracker.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/mcp_/system_tools.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/mcp_prompts/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/mcp_prompts/hook_creator.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/messaging/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/messaging/bus.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/messaging/commands.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/messaging/markdown_patches.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/messaging/message_queue.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/messaging/messages.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/messaging/pause_controller.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/messaging/queue_console.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/messaging/renderers.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/messaging/rich_renderer.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/messaging/spinner/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/messaging/spinner/console_spinner.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/messaging/spinner/spinner_base.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/messaging/subagent_console.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/model_descriptions.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/model_factory.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/model_switching.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/model_utils.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/models.json +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/models_dev_api.json +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/models_dev_parser.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/agent_skills/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/agent_skills/config.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/agent_skills/discovery.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/agent_skills/downloader.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/agent_skills/enabled_skills.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/agent_skills/installer.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/agent_skills/metadata.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/agent_skills/prompt_builder.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/agent_skills/register_callbacks.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/agent_skills/remote_catalog.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/agent_skills/skill_catalog.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/agent_skills/skill_commands.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/agent_skills/skills_install_menu.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/agent_skills/skills_menu.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/agent_steering/README.md +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/agent_steering/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/agent_steering/line_editor.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/agent_steering/register_callbacks.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/agent_steering/steering_prompt.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/aws_bedrock/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/aws_bedrock/config.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/aws_bedrock/register_callbacks.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/aws_bedrock/utils.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/azure_foundry/README.md +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/azure_foundry/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/azure_foundry/config.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/azure_foundry/discovery.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/azure_foundry/register_callbacks.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/azure_foundry/token.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/azure_foundry/utils.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/chatgpt_oauth/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/chatgpt_oauth/config.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/chatgpt_oauth/oauth_flow.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/chatgpt_oauth/register_callbacks.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/chatgpt_oauth/test_plugin.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/chatgpt_oauth/utils.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/claude_code_hooks/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/claude_code_hooks/config.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/claude_code_hooks/register_callbacks.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/claude_code_oauth/README.md +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/claude_code_oauth/SETUP.md +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/claude_code_oauth/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/claude_code_oauth/config.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/claude_code_oauth/fast_mode.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/claude_code_oauth/prompt_handler.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/claude_code_oauth/register_callbacks.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/claude_code_oauth/test_fast_mode.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/claude_code_oauth/test_plugin.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/claude_code_oauth/token_refresh_heartbeat.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/claude_code_oauth/utils.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/code_puppy_agent/SKILL.md +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/code_puppy_agent/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/code_puppy_agent/register_callbacks.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/config.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/context_indicator/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/context_indicator/register_callbacks.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/context_indicator/usage.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/copilot_auth/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/copilot_auth/config.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/copilot_auth/reasoning_client.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/copilot_auth/register_callbacks.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/copilot_auth/utils.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/customizable_commands/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/customizable_commands/register_callbacks.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/dbos_durable_exec/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/dbos_durable_exec/cancel.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/dbos_durable_exec/commands.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/dbos_durable_exec/config.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/dbos_durable_exec/lifecycle.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/dbos_durable_exec/register_callbacks.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/dbos_durable_exec/runtime.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/dbos_durable_exec/startup_lock.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/dbos_durable_exec/workflow_ids.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/dbos_durable_exec/wrapper.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/destructive_command_guard/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/destructive_command_guard/detector.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/destructive_command_guard/register_callbacks.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/emoji_filter/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/emoji_filter/config.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/emoji_filter/register_callbacks.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/emoji_filter/stripper.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/example_custom_command/README.md +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/example_custom_command/register_callbacks.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/file_permission_handler/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/file_permission_handler/register_callbacks.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/force_push_guard/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/force_push_guard/detector.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/force_push_guard/register_callbacks.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/force_push_guard/test_detector.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/frontend_emitter/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/frontend_emitter/emitter.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/frontend_emitter/register_callbacks.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/frontend_emitter/session_context.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/hook_creator/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/hook_creator/register_callbacks.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/hook_manager/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/hook_manager/config.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/hook_manager/hooks_menu.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/hook_manager/register_callbacks.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/oauth_puppy_html.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/obsidian_agent/README.md +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/obsidian_agent/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/obsidian_agent/agent_obsidian.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/obsidian_agent/register_callbacks.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/ollama/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/ollama/register_callbacks.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/ollama_setup/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/ollama_setup/completer.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/ollama_setup/register_callbacks.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/plugin_list/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/plugin_list/plugins_menu.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/plugin_list/register_callbacks.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/pop_command/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/pop_command/register_callbacks.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/prompt_newline/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/prompt_newline/config.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/prompt_newline/register_callbacks.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/prune/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/prune/prune_menu.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/prune/prune_model.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/prune/prune_render.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/prune/register_callbacks.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/puppy_kennel/README.md +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/puppy_kennel/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/puppy_kennel/commands.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/puppy_kennel/config.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/puppy_kennel/kennel.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/puppy_kennel/packer.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/puppy_kennel/recorder.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/puppy_kennel/register_callbacks.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/puppy_kennel/retriever.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/puppy_kennel/schema.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/puppy_kennel/state.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/puppy_kennel/tools.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/puppy_kennel/wings.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/review_pr/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/review_pr/register_callbacks.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/shell_safety/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/shell_safety/agent_shell_safety.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/shell_safety/command_cache.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/shell_safety/register_callbacks.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/statusline/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/statusline/config.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/statusline/prompt_patch.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/statusline/register_callbacks.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/statusline/runner.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/statusline/statusline_command.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/subagent_panel/README.md +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/subagent_panel/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/subagent_panel/coalesce_patch.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/subagent_panel/register_callbacks.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/subagent_panel/resume_repaint.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/subagent_panel/state.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/switch_agent_resume/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/switch_agent_resume/register_callbacks.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/synthetic_status/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/synthetic_status/register_callbacks.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/synthetic_status/status_api.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/theme/README.md +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/theme/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/theme/bundled_palettes.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/theme/content_styles.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/theme/osc_palette.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/theme/picker.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/theme/register_callbacks.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/theme/rich_themes.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/theme/themes.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/token_ratio_learner/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/token_ratio_learner/ratios.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/token_ratio_learner/register_callbacks.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/universal_constructor/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/universal_constructor/models.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/universal_constructor/register_callbacks.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/universal_constructor/registry.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/universal_constructor/sandbox.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/wide_completion_menu/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/wide_completion_menu/register_callbacks.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/wiggum/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/wiggum/judge.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/wiggum/judge_config.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/wiggum/register_callbacks.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/plugins/wiggum/state.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/provider_credentials.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/provider_identity.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/pydantic_patches.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/reopenable_async_client.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/round_robin_model.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/session_storage.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/status_display.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/summarization_agent.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/terminal_utils.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/tools/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/tools/agent_tools.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/tools/ask_user_question/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/tools/ask_user_question/constants.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/tools/ask_user_question/demo_tui.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/tools/ask_user_question/handler.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/tools/ask_user_question/models.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/tools/ask_user_question/registration.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/tools/ask_user_question/renderers.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/tools/ask_user_question/terminal_ui.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/tools/ask_user_question/theme.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/tools/ask_user_question/tui_loop.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/tools/browser/__init__.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/tools/browser/browser_control.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/tools/browser/browser_interactions.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/tools/browser/browser_locators.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/tools/browser/browser_manager.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/tools/browser/browser_navigation.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/tools/browser/browser_screenshot.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/tools/browser/browser_scripts.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/tools/browser/browser_workflows.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/tools/command_runner.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/tools/common.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/tools/display.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/tools/file_operations.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/tools/image_tools.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/tools/model_tools.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/tools/skills_tools.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/tools/subagent_context.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/tools/subagent_invocation.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/tools/tools_content.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/tools/universal_constructor.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/uvx_detection.py +0 -0
- {code_puppy-0.0.575 → code_puppy-0.0.577}/code_puppy/version_checker.py +0 -0
|
@@ -133,6 +133,48 @@ def _resume_session_from_path(raw_path: str) -> None:
|
|
|
133
133
|
)
|
|
134
134
|
|
|
135
135
|
|
|
136
|
+
def apply_quick_resume(args) -> bool:
|
|
137
|
+
"""Resolve ``--quick-resume [PATH]`` into ``args.resume`` so the existing
|
|
138
|
+
resume machinery loads it.
|
|
139
|
+
|
|
140
|
+
Looks up the most recent autosave for PATH (defaulting to cwd), scoped to
|
|
141
|
+
the nearest git worktree root + branch when available, with a no-git
|
|
142
|
+
fallback. No-op when ``--quick-resume`` was not requested or ``--resume`` is
|
|
143
|
+
already set (explicit ``--resume`` always wins). Returns True when a target
|
|
144
|
+
was resolved.
|
|
145
|
+
"""
|
|
146
|
+
existing_resume = getattr(args, "resume", None)
|
|
147
|
+
quick_resume_target = getattr(args, "quick_resume", None)
|
|
148
|
+
if quick_resume_target is None or (
|
|
149
|
+
existing_resume and str(existing_resume).strip()
|
|
150
|
+
):
|
|
151
|
+
return False
|
|
152
|
+
|
|
153
|
+
from code_puppy.config import (
|
|
154
|
+
format_quick_resume_scope,
|
|
155
|
+
get_quick_resume_location,
|
|
156
|
+
resolve_quick_resume_pickle,
|
|
157
|
+
)
|
|
158
|
+
from code_puppy.messaging import emit_info
|
|
159
|
+
|
|
160
|
+
target_path = str(quick_resume_target).strip() or "."
|
|
161
|
+
|
|
162
|
+
# Diagnostic identifies the lookup scope without leaking full local paths.
|
|
163
|
+
cwd, branch = get_quick_resume_location(target_path)
|
|
164
|
+
emit_info(
|
|
165
|
+
"\U0001f50d Quick Resume selected - finding latest session for "
|
|
166
|
+
f"{format_quick_resume_scope(cwd, branch)}"
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
quick_resume_pickle = resolve_quick_resume_pickle(target_path)
|
|
170
|
+
if quick_resume_pickle:
|
|
171
|
+
args.resume = quick_resume_pickle
|
|
172
|
+
return True
|
|
173
|
+
|
|
174
|
+
emit_info("No previous session found for this scope; starting fresh.")
|
|
175
|
+
return False
|
|
176
|
+
|
|
177
|
+
|
|
136
178
|
async def main():
|
|
137
179
|
"""Main async entry point for Code Puppy CLI."""
|
|
138
180
|
parser = argparse.ArgumentParser(description="Code Puppy - A code generation agent")
|
|
@@ -174,6 +216,18 @@ async def main():
|
|
|
174
216
|
metavar="PATH",
|
|
175
217
|
help="Resume a saved session from a .pkl file (e.g. ~/.code_puppy/contexts/foo.pkl)",
|
|
176
218
|
)
|
|
219
|
+
parser.add_argument(
|
|
220
|
+
"--quick-resume",
|
|
221
|
+
"-qr",
|
|
222
|
+
nargs="?",
|
|
223
|
+
const=".",
|
|
224
|
+
default=None,
|
|
225
|
+
metavar="PATH",
|
|
226
|
+
help=(
|
|
227
|
+
"Resume the most recent session for PATH (defaults to the current "
|
|
228
|
+
"directory; scopes to git root + branch when available)"
|
|
229
|
+
),
|
|
230
|
+
)
|
|
177
231
|
parser.add_argument(
|
|
178
232
|
"command", nargs="*", help="Run a single command (deprecated, use -p instead)"
|
|
179
233
|
)
|
|
@@ -395,6 +449,10 @@ async def main():
|
|
|
395
449
|
|
|
396
450
|
await callbacks.on_startup()
|
|
397
451
|
|
|
452
|
+
# Resolve --quick-resume [PATH] into --resume so the resume machinery below
|
|
453
|
+
# loads the most recent session for that canonical (git-root + branch) scope.
|
|
454
|
+
apply_quick_resume(args)
|
|
455
|
+
|
|
398
456
|
if args.resume:
|
|
399
457
|
_resume_session_from_path(args.resume)
|
|
400
458
|
|
|
@@ -20,9 +20,15 @@ from prompt_toolkit.widgets import Frame
|
|
|
20
20
|
from rich.console import Console
|
|
21
21
|
from rich.markdown import Markdown
|
|
22
22
|
|
|
23
|
+
from code_puppy.command_line.autosave_search import (
|
|
24
|
+
SessionContentIndex,
|
|
25
|
+
entry_matches,
|
|
26
|
+
iter_alphabet_bindings,
|
|
27
|
+
)
|
|
23
28
|
from code_puppy.command_line.pagination import (
|
|
24
29
|
ensure_visible_page,
|
|
25
30
|
get_page_bounds,
|
|
31
|
+
get_page_for_index,
|
|
26
32
|
get_total_pages,
|
|
27
33
|
)
|
|
28
34
|
from code_puppy.config import AUTOSAVE_DIR
|
|
@@ -163,17 +169,40 @@ def _render_menu_panel(
|
|
|
163
169
|
page: int,
|
|
164
170
|
selected_idx: int,
|
|
165
171
|
browse_mode: bool = False,
|
|
172
|
+
search_text: str = "",
|
|
173
|
+
in_search_mode: bool = False,
|
|
174
|
+
search_buffer: str = "",
|
|
175
|
+
status_line: Optional[Tuple[str, str]] = None,
|
|
166
176
|
) -> List:
|
|
167
|
-
"""Render the left menu panel with pagination.
|
|
177
|
+
"""Render the left menu panel with pagination.
|
|
178
|
+
|
|
179
|
+
``status_line`` is an optional ``(style, text)`` pair that, when
|
|
180
|
+
provided, replaces the normal search/filter indicator. The picker
|
|
181
|
+
uses it to surface transient states like ``Filtering...`` (right
|
|
182
|
+
after the user commits a search) and ``Indexing N/M...`` (while the
|
|
183
|
+
background pre-warm task is still chewing through sessions).
|
|
184
|
+
"""
|
|
168
185
|
lines = []
|
|
169
186
|
total_pages = get_total_pages(len(entries), PAGE_SIZE)
|
|
170
187
|
start_idx, end_idx = get_page_bounds(page, len(entries), PAGE_SIZE)
|
|
171
188
|
|
|
172
189
|
lines.append(("", f" Session Page(s): ({page + 1}/{total_pages})"))
|
|
190
|
+
if status_line is not None:
|
|
191
|
+
lines.append(("", "\n"))
|
|
192
|
+
lines.append(status_line)
|
|
193
|
+
elif in_search_mode:
|
|
194
|
+
lines.append(("", "\n"))
|
|
195
|
+
lines.append(("fg:ansiyellow", f" Searching: '{search_buffer}'"))
|
|
196
|
+
elif search_text:
|
|
197
|
+
lines.append(("", "\n"))
|
|
198
|
+
lines.append(("fg:ansiyellow", f" Filter: '{search_text}'"))
|
|
173
199
|
lines.append(("", "\n\n"))
|
|
174
200
|
|
|
175
201
|
if not entries:
|
|
176
|
-
|
|
202
|
+
if search_text or in_search_mode:
|
|
203
|
+
lines.append(("fg:yellow", " No sessions match your search."))
|
|
204
|
+
else:
|
|
205
|
+
lines.append(("fg:yellow", " No autosave sessions found."))
|
|
177
206
|
lines.append(("", "\n\n"))
|
|
178
207
|
# Navigation hints (always show)
|
|
179
208
|
lines.append(("", "\n"))
|
|
@@ -225,6 +254,8 @@ def _render_menu_panel(
|
|
|
225
254
|
lines.append(("", "Page\n"))
|
|
226
255
|
lines.append(("fg:ansicyan", " e "))
|
|
227
256
|
lines.append(("", "Browse msgs\n"))
|
|
257
|
+
lines.append(("fg:ansibrightblack", " / "))
|
|
258
|
+
lines.append(("", "Search content\n"))
|
|
228
259
|
lines.append(("fg:green", " Enter "))
|
|
229
260
|
lines.append(("", "Load\n"))
|
|
230
261
|
lines.append(("fg:ansibrightred", " Ctrl+C "))
|
|
@@ -515,7 +546,7 @@ async def interactive_autosave_picker() -> Optional[str]:
|
|
|
515
546
|
return None
|
|
516
547
|
|
|
517
548
|
# State
|
|
518
|
-
selected_idx = [0] # Current selection (
|
|
549
|
+
selected_idx = [0] # Current selection (index into visible_entries)
|
|
519
550
|
current_page = [0] # Current page
|
|
520
551
|
result = [None] # Selected session name
|
|
521
552
|
|
|
@@ -524,21 +555,90 @@ async def interactive_autosave_picker() -> Optional[str]:
|
|
|
524
555
|
message_idx = [0] # Current message index (0 = most recent)
|
|
525
556
|
cached_history = [None] # Cached history for current session in browse mode
|
|
526
557
|
|
|
527
|
-
|
|
558
|
+
# Search/filter state (mirrors set_menu.py's `/`-search UX)
|
|
559
|
+
search_text = [""] # Committed filter (drives visible_entries)
|
|
560
|
+
in_search_mode = [False] # Currently typing into the search buffer?
|
|
561
|
+
search_buffer = [""] # Live keystrokes before Enter commits them
|
|
562
|
+
visible_entries: List[List[Tuple[str, dict]]] = [list(entries)]
|
|
563
|
+
content_index = SessionContentIndex() # Lazy content cache for THIS picker
|
|
564
|
+
is_filtering = [False] # True while the Enter-handler is doing the work
|
|
565
|
+
total_to_index = len(entries) # Denominator for the prewarm progress hint
|
|
528
566
|
|
|
529
567
|
def get_current_entry() -> Optional[Tuple[str, dict]]:
|
|
530
|
-
|
|
531
|
-
|
|
568
|
+
visible = visible_entries[0]
|
|
569
|
+
if 0 <= selected_idx[0] < len(visible):
|
|
570
|
+
return visible[selected_idx[0]]
|
|
532
571
|
return None
|
|
533
572
|
|
|
573
|
+
def _filter_entries(needle: str) -> List[Tuple[str, dict]]:
|
|
574
|
+
"""Pure filter: needle in, filtered list out. Safe to run off-thread.
|
|
575
|
+
|
|
576
|
+
Reads ``entries``, ``content_index``, and ``base_dir`` from the
|
|
577
|
+
enclosing closure -- ``entries`` is built once and never mutated,
|
|
578
|
+
and ``content_index`` is protected by its own internal lock, so
|
|
579
|
+
this is safe to invoke from an ``asyncio.to_thread`` worker.
|
|
580
|
+
"""
|
|
581
|
+
if not needle:
|
|
582
|
+
return list(entries)
|
|
583
|
+
return [e for e in entries if entry_matches(e, needle, content_index, base_dir)]
|
|
584
|
+
|
|
585
|
+
def _apply_filter_result(filtered: List[Tuple[str, dict]]) -> None:
|
|
586
|
+
"""Apply a filter result to picker state. Must run on the main thread."""
|
|
587
|
+
visible_entries[0] = filtered
|
|
588
|
+
if not filtered:
|
|
589
|
+
selected_idx[0] = 0
|
|
590
|
+
current_page[0] = 0
|
|
591
|
+
return
|
|
592
|
+
selected_idx[0] = min(selected_idx[0], len(filtered) - 1)
|
|
593
|
+
current_page[0] = get_page_for_index(selected_idx[0], PAGE_SIZE)
|
|
594
|
+
|
|
595
|
+
def update_visible_entries() -> None:
|
|
596
|
+
"""Synchronous re-filter -- only safe when the cache is warm or empty.
|
|
597
|
+
|
|
598
|
+
Used for the picker's initial setup (no filter active -> trivial)
|
|
599
|
+
and as a fallback. The post-Enter path goes through
|
|
600
|
+
:func:`asyncio.to_thread` instead so a cold-cache filter does not
|
|
601
|
+
freeze the event loop.
|
|
602
|
+
"""
|
|
603
|
+
_apply_filter_result(_filter_entries(search_text[0]))
|
|
604
|
+
|
|
534
605
|
# Build UI
|
|
535
606
|
menu_control = FormattedTextControl(text="")
|
|
536
607
|
preview_control = FormattedTextControl(text="")
|
|
537
608
|
|
|
609
|
+
def _compute_status_line() -> Optional[Tuple[str, str]]:
|
|
610
|
+
"""Resolve which transient indicator (if any) takes the header slot.
|
|
611
|
+
|
|
612
|
+
Priority order:
|
|
613
|
+
1. ``Filtering...`` -- user just hit Enter, filter is running.
|
|
614
|
+
2. ``Searching: '...'`` / ``Filter: '...'`` -- normal search UX,
|
|
615
|
+
handled by the renderer's own branches when this returns None.
|
|
616
|
+
3. ``Indexing N/M...`` -- background pre-warm in progress and no
|
|
617
|
+
other search activity to crowd out.
|
|
618
|
+
"""
|
|
619
|
+
if is_filtering[0]:
|
|
620
|
+
return ("fg:ansicyan bold", " Filtering...")
|
|
621
|
+
if in_search_mode[0] or search_text[0]:
|
|
622
|
+
return None # Let the renderer show the search/filter line.
|
|
623
|
+
cached = content_index.count()
|
|
624
|
+
if 0 < cached < total_to_index:
|
|
625
|
+
return (
|
|
626
|
+
"fg:ansibrightblack",
|
|
627
|
+
f" Indexing {cached}/{total_to_index}...",
|
|
628
|
+
)
|
|
629
|
+
return None
|
|
630
|
+
|
|
538
631
|
def update_display():
|
|
539
632
|
"""Update both panels."""
|
|
540
633
|
menu_control.text = _render_menu_panel(
|
|
541
|
-
|
|
634
|
+
visible_entries[0],
|
|
635
|
+
current_page[0],
|
|
636
|
+
selected_idx[0],
|
|
637
|
+
browse_mode[0],
|
|
638
|
+
search_text=search_text[0],
|
|
639
|
+
in_search_mode=in_search_mode[0],
|
|
640
|
+
search_buffer=search_buffer[0],
|
|
641
|
+
status_line=_compute_status_line(),
|
|
542
642
|
)
|
|
543
643
|
# Show message browser if in browse mode, otherwise show preview
|
|
544
644
|
if browse_mode[0] and cached_history[0] is not None:
|
|
@@ -574,6 +674,8 @@ async def interactive_autosave_picker() -> Optional[str]:
|
|
|
574
674
|
@kb.add("up")
|
|
575
675
|
@kb.add("c-p") # Ctrl+P = previous (Emacs-style)
|
|
576
676
|
def _(event):
|
|
677
|
+
if in_search_mode[0]:
|
|
678
|
+
return # While typing the search buffer, arrows do nothing.
|
|
577
679
|
if browse_mode[0]:
|
|
578
680
|
# In browse mode: go to older message
|
|
579
681
|
if cached_history[0] and message_idx[0] < len(cached_history[0]) - 1:
|
|
@@ -586,7 +688,7 @@ async def interactive_autosave_picker() -> Optional[str]:
|
|
|
586
688
|
current_page[0] = ensure_visible_page(
|
|
587
689
|
selected_idx[0],
|
|
588
690
|
current_page[0],
|
|
589
|
-
len(
|
|
691
|
+
len(visible_entries[0]),
|
|
590
692
|
PAGE_SIZE,
|
|
591
693
|
)
|
|
592
694
|
update_display()
|
|
@@ -594,6 +696,8 @@ async def interactive_autosave_picker() -> Optional[str]:
|
|
|
594
696
|
@kb.add("down")
|
|
595
697
|
@kb.add("c-n") # Ctrl+N = next (Emacs-style)
|
|
596
698
|
def _(event):
|
|
699
|
+
if in_search_mode[0]:
|
|
700
|
+
return
|
|
597
701
|
if browse_mode[0]:
|
|
598
702
|
# In browse mode: go to newer message
|
|
599
703
|
if message_idx[0] > 0:
|
|
@@ -601,18 +705,20 @@ async def interactive_autosave_picker() -> Optional[str]:
|
|
|
601
705
|
update_display()
|
|
602
706
|
else:
|
|
603
707
|
# Normal mode: navigate sessions
|
|
604
|
-
if selected_idx[0] < len(
|
|
708
|
+
if selected_idx[0] < len(visible_entries[0]) - 1:
|
|
605
709
|
selected_idx[0] += 1
|
|
606
710
|
current_page[0] = ensure_visible_page(
|
|
607
711
|
selected_idx[0],
|
|
608
712
|
current_page[0],
|
|
609
|
-
len(
|
|
713
|
+
len(visible_entries[0]),
|
|
610
714
|
PAGE_SIZE,
|
|
611
715
|
)
|
|
612
716
|
update_display()
|
|
613
717
|
|
|
614
718
|
@kb.add("left")
|
|
615
719
|
def _(event):
|
|
720
|
+
if in_search_mode[0]:
|
|
721
|
+
return
|
|
616
722
|
if current_page[0] > 0:
|
|
617
723
|
current_page[0] -= 1
|
|
618
724
|
selected_idx[0] = current_page[0] * PAGE_SIZE
|
|
@@ -620,14 +726,25 @@ async def interactive_autosave_picker() -> Optional[str]:
|
|
|
620
726
|
|
|
621
727
|
@kb.add("right")
|
|
622
728
|
def _(event):
|
|
729
|
+
if in_search_mode[0]:
|
|
730
|
+
return
|
|
731
|
+
# Recompute total_pages from visible_entries every call -- filtering
|
|
732
|
+
# changes the list length and a stale captured value would let users
|
|
733
|
+
# page past the end of a filtered result.
|
|
734
|
+
total_pages = get_total_pages(len(visible_entries[0]), PAGE_SIZE)
|
|
623
735
|
if current_page[0] < total_pages - 1:
|
|
624
736
|
current_page[0] += 1
|
|
625
737
|
selected_idx[0] = current_page[0] * PAGE_SIZE
|
|
626
738
|
update_display()
|
|
627
739
|
|
|
628
740
|
@kb.add("e")
|
|
741
|
+
@kb.add("E")
|
|
629
742
|
def _(event):
|
|
630
|
-
"""Enter message browse mode."""
|
|
743
|
+
"""Enter message browse mode (or feed the search buffer)."""
|
|
744
|
+
if in_search_mode[0]:
|
|
745
|
+
search_buffer[0] += "e"
|
|
746
|
+
update_display()
|
|
747
|
+
return
|
|
631
748
|
if browse_mode[0]:
|
|
632
749
|
return # Already in browse mode
|
|
633
750
|
entry = get_current_entry()
|
|
@@ -643,20 +760,30 @@ async def interactive_autosave_picker() -> Optional[str]:
|
|
|
643
760
|
|
|
644
761
|
@kb.add("escape")
|
|
645
762
|
def _(event):
|
|
646
|
-
"""Exit browse mode or cancel."""
|
|
763
|
+
"""Exit search mode, browse mode, or cancel -- in that priority."""
|
|
764
|
+
if in_search_mode[0]:
|
|
765
|
+
in_search_mode[0] = False
|
|
766
|
+
search_buffer[0] = ""
|
|
767
|
+
update_display()
|
|
768
|
+
return
|
|
647
769
|
if browse_mode[0]:
|
|
648
770
|
browse_mode[0] = False
|
|
649
771
|
cached_history[0] = None
|
|
650
772
|
message_idx[0] = 0
|
|
651
773
|
update_display()
|
|
652
774
|
else:
|
|
653
|
-
# Not in
|
|
775
|
+
# Not in any sub-mode - treat as cancel
|
|
654
776
|
result[0] = None
|
|
655
777
|
event.app.exit()
|
|
656
778
|
|
|
657
779
|
@kb.add("q")
|
|
780
|
+
@kb.add("Q")
|
|
658
781
|
def _(event):
|
|
659
|
-
"""
|
|
782
|
+
"""Feed the search buffer, or exit browse mode if not searching."""
|
|
783
|
+
if in_search_mode[0]:
|
|
784
|
+
search_buffer[0] += "q"
|
|
785
|
+
update_display()
|
|
786
|
+
return
|
|
660
787
|
if browse_mode[0]:
|
|
661
788
|
browse_mode[0] = False
|
|
662
789
|
cached_history[0] = None
|
|
@@ -664,12 +791,57 @@ async def interactive_autosave_picker() -> Optional[str]:
|
|
|
664
791
|
update_display()
|
|
665
792
|
|
|
666
793
|
@kb.add("enter")
|
|
667
|
-
def _(event):
|
|
794
|
+
async def _(event):
|
|
795
|
+
if in_search_mode[0]:
|
|
796
|
+
# Commit the buffer as the active filter. Repaint "Filtering..."
|
|
797
|
+
# BEFORE doing the work so users get feedback even when the
|
|
798
|
+
# content index is cold and the lookup has to read pickles.
|
|
799
|
+
search_text[0] = search_buffer[0]
|
|
800
|
+
in_search_mode[0] = False
|
|
801
|
+
search_buffer[0] = ""
|
|
802
|
+
is_filtering[0] = True
|
|
803
|
+
update_display()
|
|
804
|
+
event.app.invalidate()
|
|
805
|
+
# Run the (potentially blocking) filter on a worker thread
|
|
806
|
+
# so the event loop stays responsive. ``await`` yields here,
|
|
807
|
+
# which also gives prompt_toolkit the tick it needs to paint
|
|
808
|
+
# the "Filtering..." indicator before the worker starts.
|
|
809
|
+
try:
|
|
810
|
+
filtered = await asyncio.to_thread(_filter_entries, search_text[0])
|
|
811
|
+
_apply_filter_result(filtered)
|
|
812
|
+
finally:
|
|
813
|
+
is_filtering[0] = False
|
|
814
|
+
update_display()
|
|
815
|
+
event.app.invalidate()
|
|
816
|
+
return
|
|
668
817
|
entry = get_current_entry()
|
|
669
818
|
if entry:
|
|
670
819
|
result[0] = entry[0] # Store session name
|
|
671
820
|
event.app.exit()
|
|
672
821
|
|
|
822
|
+
@kb.add("/")
|
|
823
|
+
def _(event):
|
|
824
|
+
"""Enter search mode. Disabled inside browse mode (focus is on msgs)."""
|
|
825
|
+
if browse_mode[0]:
|
|
826
|
+
return
|
|
827
|
+
in_search_mode[0] = True
|
|
828
|
+
search_buffer[0] = ""
|
|
829
|
+
update_display()
|
|
830
|
+
|
|
831
|
+
@kb.add("backspace")
|
|
832
|
+
def _(event):
|
|
833
|
+
if in_search_mode[0]:
|
|
834
|
+
search_buffer[0] = search_buffer[0][:-1]
|
|
835
|
+
update_display()
|
|
836
|
+
|
|
837
|
+
for _key, _append in iter_alphabet_bindings():
|
|
838
|
+
|
|
839
|
+
@kb.add(_key)
|
|
840
|
+
def _alpha(event, _c=_append):
|
|
841
|
+
if in_search_mode[0]:
|
|
842
|
+
search_buffer[0] += _c
|
|
843
|
+
update_display()
|
|
844
|
+
|
|
673
845
|
@kb.add("c-c")
|
|
674
846
|
def _(event):
|
|
675
847
|
result[0] = None
|
|
@@ -683,6 +855,32 @@ async def interactive_autosave_picker() -> Optional[str]:
|
|
|
683
855
|
mouse_support=False,
|
|
684
856
|
)
|
|
685
857
|
|
|
858
|
+
async def _prewarm_index() -> None:
|
|
859
|
+
"""Eagerly populate the content index in the background.
|
|
860
|
+
|
|
861
|
+
Without this, the first content-search blocks the UI on N pickle
|
|
862
|
+
reads. Running the loads on a worker thread keeps the event loop
|
|
863
|
+
responsive; ``app.invalidate()`` after each one drives the
|
|
864
|
+
``Indexing N/M...`` progress hint in the menu header.
|
|
865
|
+
"""
|
|
866
|
+
for name, _meta in entries:
|
|
867
|
+
if name in content_index:
|
|
868
|
+
continue
|
|
869
|
+
try:
|
|
870
|
+
await asyncio.to_thread(content_index.lookup, name, base_dir)
|
|
871
|
+
except asyncio.CancelledError:
|
|
872
|
+
raise
|
|
873
|
+
except Exception:
|
|
874
|
+
# SessionContentIndex.lookup already swallows + caches
|
|
875
|
+
# load errors; we should never get here, but be paranoid.
|
|
876
|
+
pass
|
|
877
|
+
try:
|
|
878
|
+
app.invalidate()
|
|
879
|
+
except Exception:
|
|
880
|
+
# If the app is tearing down, invalidate may explode --
|
|
881
|
+
# not our problem, just stop pre-warming gracefully.
|
|
882
|
+
return
|
|
883
|
+
|
|
686
884
|
set_awaiting_user_input(True)
|
|
687
885
|
|
|
688
886
|
# Enter alternate screen buffer once for entire session
|
|
@@ -691,6 +889,8 @@ async def interactive_autosave_picker() -> Optional[str]:
|
|
|
691
889
|
sys.stdout.flush()
|
|
692
890
|
await asyncio.sleep(0.05)
|
|
693
891
|
|
|
892
|
+
prewarm_task = asyncio.create_task(_prewarm_index())
|
|
893
|
+
|
|
694
894
|
try:
|
|
695
895
|
# Initial display
|
|
696
896
|
update_display()
|
|
@@ -703,6 +903,13 @@ async def interactive_autosave_picker() -> Optional[str]:
|
|
|
703
903
|
await app.run_async()
|
|
704
904
|
|
|
705
905
|
finally:
|
|
906
|
+
# Cancel the background pre-warm if it hasn't finished; suppress
|
|
907
|
+
# the resulting CancelledError so the picker exits cleanly.
|
|
908
|
+
prewarm_task.cancel()
|
|
909
|
+
try:
|
|
910
|
+
await prewarm_task
|
|
911
|
+
except (asyncio.CancelledError, Exception):
|
|
912
|
+
pass
|
|
706
913
|
# Exit alternate screen buffer once at end
|
|
707
914
|
sys.stdout.write("\033[?1049l") # Exit alternate buffer
|
|
708
915
|
sys.stdout.flush()
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
"""Search/filter helpers for the ``/resume`` (autosave_load) session picker.
|
|
2
|
+
|
|
3
|
+
Lives separately from :mod:`code_puppy.command_line.autosave_menu` so that
|
|
4
|
+
file (already overweight at ~700+ lines) does not grow further. Everything
|
|
5
|
+
in here is pure / IO-tolerant so it is unit-testable without spinning up
|
|
6
|
+
a prompt_toolkit ``Application``.
|
|
7
|
+
|
|
8
|
+
The picker UX mirrors ``/set``: pressing ``/`` enters search mode, alphabet
|
|
9
|
+
chars append to a buffer, ``Enter`` commits the buffer, ``Esc`` cancels the
|
|
10
|
+
search. The novel bit is that this picker filters on *session content* --
|
|
11
|
+
the concatenated text of every message in each session -- rather than just
|
|
12
|
+
the timestamp/metadata visible in the left menu. Content is loaded lazily
|
|
13
|
+
and cached for the picker's lifetime so we do not re-unpickle session
|
|
14
|
+
files on every keystroke.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
from datetime import datetime
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
from threading import Lock
|
|
22
|
+
from typing import Callable, Dict, Iterator, Optional, Tuple
|
|
23
|
+
|
|
24
|
+
from code_puppy.session_storage import load_session
|
|
25
|
+
|
|
26
|
+
# Alphabet bound to the search buffer while ``in_search_mode`` is True.
|
|
27
|
+
# Lowercase ASCII letters (except ``e`` and ``q``), digits, underscore,
|
|
28
|
+
# hyphen, and space. ``e`` and ``q`` are deliberately excluded because
|
|
29
|
+
# autosave_menu.py already binds them to nav actions (browse-msgs and
|
|
30
|
+
# exit-browse respectively); those existing handlers are dual-mode --
|
|
31
|
+
# they append to the search buffer when ``in_search_mode`` is True and
|
|
32
|
+
# fire their nav action otherwise. Same trick set_menu uses for ``r``.
|
|
33
|
+
SEARCH_ALPHABET = "abcdfghijklmnoprstuvwxyz0123456789_- "
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def iter_alphabet_bindings() -> Iterator[Tuple[str, str]]:
|
|
37
|
+
"""Yield ``(key_to_bind, char_to_append)`` pairs for the alphabet.
|
|
38
|
+
|
|
39
|
+
For each lowercase character in :data:`SEARCH_ALPHABET` we yield both
|
|
40
|
+
the lowercase and (when distinct) the uppercase form. Both forms
|
|
41
|
+
append the *lowercase* character so the buffer stays case-folded --
|
|
42
|
+
search match is already case-insensitive, and folding on input means
|
|
43
|
+
the rendered ``Searching: '...'`` line is stable regardless of
|
|
44
|
+
Shift/Caps Lock state.
|
|
45
|
+
|
|
46
|
+
Yields lowercase before uppercase so prompt_toolkit's keybinding
|
|
47
|
+
registration order is deterministic and easy to reason about.
|
|
48
|
+
"""
|
|
49
|
+
for ch in SEARCH_ALPHABET:
|
|
50
|
+
yield ch, ch
|
|
51
|
+
upper = ch.upper()
|
|
52
|
+
if upper != ch:
|
|
53
|
+
yield upper, ch
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _session_text(history: list) -> str:
|
|
57
|
+
"""Concatenate the raw text content of every message part.
|
|
58
|
+
|
|
59
|
+
Deliberately uses raw ``part.content`` (only when it is a non-empty
|
|
60
|
+
``str``) instead of going through
|
|
61
|
+
:func:`code_puppy.command_line.autosave_menu._extract_message_content`.
|
|
62
|
+
That helper decorates tool messages with ``"Tool Call: <name>"`` and
|
|
63
|
+
``"Tool Result: <name>"`` prefixes -- if we indexed the decorated
|
|
64
|
+
form, typing ``tool call`` would match every session that ever
|
|
65
|
+
called a tool. Noise, not signal.
|
|
66
|
+
"""
|
|
67
|
+
chunks: list = []
|
|
68
|
+
for msg in history:
|
|
69
|
+
for part in getattr(msg, "parts", ()) or ():
|
|
70
|
+
content = getattr(part, "content", None)
|
|
71
|
+
if isinstance(content, str) and content:
|
|
72
|
+
chunks.append(content)
|
|
73
|
+
return "\n".join(chunks)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class SessionContentIndex:
|
|
77
|
+
"""Lazy, per-picker cache of ``{session_name -> lowercased text}``.
|
|
78
|
+
|
|
79
|
+
Loading a session unpickles the whole file, so we only do it on
|
|
80
|
+
demand and cache the result for the lifetime of one picker
|
|
81
|
+
invocation. Errors are tolerated and cached as empty strings so we
|
|
82
|
+
do not re-hit broken files on every keystroke.
|
|
83
|
+
|
|
84
|
+
The ``loader`` injection point exists for testing -- production code
|
|
85
|
+
always uses :func:`code_puppy.session_storage.load_session`.
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
def __init__(
|
|
89
|
+
self,
|
|
90
|
+
loader: Optional[Callable[[str, Path], list]] = None,
|
|
91
|
+
) -> None:
|
|
92
|
+
self._loader = loader or load_session
|
|
93
|
+
self._cache: Dict[str, str] = {}
|
|
94
|
+
# The pre-warm task runs on an ``asyncio.to_thread`` worker while
|
|
95
|
+
# the picker's render path reads cache state on the event loop;
|
|
96
|
+
# the post-Enter filter (PR review accept #2) also runs on a
|
|
97
|
+
# worker. CPython's GIL makes individual dict ops atomic, but
|
|
98
|
+
# the ``check-then-load-then-store`` sequence in ``lookup`` is
|
|
99
|
+
# not -- the lock keeps the invariant tidy and survives a future
|
|
100
|
+
# free-threaded build where GIL atomicity disappears.
|
|
101
|
+
self._lock = Lock()
|
|
102
|
+
|
|
103
|
+
def lookup(self, session_name: str, base_dir: Path) -> str:
|
|
104
|
+
with self._lock:
|
|
105
|
+
cached = self._cache.get(session_name)
|
|
106
|
+
if cached is not None or session_name in self._cache:
|
|
107
|
+
# ``cached is not None`` short-circuits the common case;
|
|
108
|
+
# the explicit ``in`` check picks up cached-empty-string
|
|
109
|
+
# entries (failed loads) without re-running the loader.
|
|
110
|
+
return cached or ""
|
|
111
|
+
# Loader runs OUTSIDE the lock so a slow pickle read does not
|
|
112
|
+
# block other threads from checking the cache for unrelated keys.
|
|
113
|
+
try:
|
|
114
|
+
history = self._loader(session_name, base_dir)
|
|
115
|
+
text = _session_text(history).lower()
|
|
116
|
+
except Exception:
|
|
117
|
+
# Cache the failure so a broken pickle does not slow every keystroke.
|
|
118
|
+
text = ""
|
|
119
|
+
with self._lock:
|
|
120
|
+
# Last-writer-wins. If a concurrent ``lookup`` for the same
|
|
121
|
+
# key beat us here that is fine -- the value is identical.
|
|
122
|
+
self._cache[session_name] = text
|
|
123
|
+
return text
|
|
124
|
+
|
|
125
|
+
def count(self) -> int:
|
|
126
|
+
"""Number of sessions cached so far (success or failure).
|
|
127
|
+
|
|
128
|
+
The picker uses this to render an ``Indexing N/M…`` progress hint
|
|
129
|
+
while the background pre-warm task is still running.
|
|
130
|
+
"""
|
|
131
|
+
with self._lock:
|
|
132
|
+
return len(self._cache)
|
|
133
|
+
|
|
134
|
+
def __contains__(self, session_name: object) -> bool:
|
|
135
|
+
"""Membership check so callers don't have to peek at ``_cache``.
|
|
136
|
+
|
|
137
|
+
Used by the picker's pre-warm loop to skip sessions that were
|
|
138
|
+
already loaded on demand by an earlier ``lookup``.
|
|
139
|
+
"""
|
|
140
|
+
with self._lock:
|
|
141
|
+
return session_name in self._cache
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def _formatted_timestamp(metadata: dict) -> str:
|
|
145
|
+
"""Mirror the ``YYYY-MM-DD HH:MM`` format the left menu shows.
|
|
146
|
+
|
|
147
|
+
Search must hit what the user *sees*, so the formatting has to
|
|
148
|
+
match :func:`autosave_menu._render_menu_panel`.
|
|
149
|
+
"""
|
|
150
|
+
timestamp = metadata.get("timestamp", "")
|
|
151
|
+
try:
|
|
152
|
+
dt = datetime.fromisoformat(timestamp)
|
|
153
|
+
return dt.strftime("%Y-%m-%d %H:%M")
|
|
154
|
+
except (ValueError, TypeError):
|
|
155
|
+
return str(timestamp)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def entry_matches(
|
|
159
|
+
entry: Tuple[str, dict],
|
|
160
|
+
needle: str,
|
|
161
|
+
index: SessionContentIndex,
|
|
162
|
+
base_dir: Path,
|
|
163
|
+
) -> bool:
|
|
164
|
+
"""Return True if ``needle`` is a substring of the session.
|
|
165
|
+
|
|
166
|
+
Empty needle matches everything (the picker shows the full list).
|
|
167
|
+
Cheap metadata checks (session name, formatted timestamp, message
|
|
168
|
+
count) run first; the full session content is only loaded via
|
|
169
|
+
``index`` if the cheap checks miss. Typing ``2026-06`` therefore
|
|
170
|
+
never triggers a single pickle read.
|
|
171
|
+
"""
|
|
172
|
+
if not needle:
|
|
173
|
+
return True
|
|
174
|
+
needle_lower = needle.lower()
|
|
175
|
+
session_name, metadata = entry
|
|
176
|
+
|
|
177
|
+
if needle_lower in session_name.lower():
|
|
178
|
+
return True
|
|
179
|
+
if needle_lower in _formatted_timestamp(metadata).lower():
|
|
180
|
+
return True
|
|
181
|
+
if needle_lower in str(metadata.get("message_count", "")).lower():
|
|
182
|
+
return True
|
|
183
|
+
|
|
184
|
+
return needle_lower in index.lookup(session_name, base_dir)
|