code-puppy-core-plugins 0.0.2__py3-none-any.whl
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_core_plugins/__init__.py +5 -0
- code_puppy_core_plugins/acp/README.md +266 -0
- code_puppy_core_plugins/acp/__init__.py +8 -0
- code_puppy_core_plugins/acp/agent.py +494 -0
- code_puppy_core_plugins/acp/bridge.py +309 -0
- code_puppy_core_plugins/acp/capabilities.py +73 -0
- code_puppy_core_plugins/acp/commands.py +116 -0
- code_puppy_core_plugins/acp/content.py +115 -0
- code_puppy_core_plugins/acp/io_delegation.py +231 -0
- code_puppy_core_plugins/acp/mcp_config.py +66 -0
- code_puppy_core_plugins/acp/permissions.py +159 -0
- code_puppy_core_plugins/acp/persistence.py +197 -0
- code_puppy_core_plugins/acp/register_callbacks.py +110 -0
- code_puppy_core_plugins/acp/replay.py +114 -0
- code_puppy_core_plugins/acp/session.py +329 -0
- code_puppy_core_plugins/acp/session_config.py +179 -0
- code_puppy_core_plugins/acp/state.py +135 -0
- code_puppy_core_plugins/agent_skills/__init__.py +22 -0
- code_puppy_core_plugins/agent_skills/config.py +208 -0
- code_puppy_core_plugins/agent_skills/discovery.py +337 -0
- code_puppy_core_plugins/agent_skills/downloader.py +392 -0
- code_puppy_core_plugins/agent_skills/enabled_skills.py +82 -0
- code_puppy_core_plugins/agent_skills/installer.py +22 -0
- code_puppy_core_plugins/agent_skills/metadata.py +219 -0
- code_puppy_core_plugins/agent_skills/prompt_builder.py +49 -0
- code_puppy_core_plugins/agent_skills/provider.py +52 -0
- code_puppy_core_plugins/agent_skills/register_callbacks.py +309 -0
- code_puppy_core_plugins/agent_skills/remote_catalog.py +322 -0
- code_puppy_core_plugins/agent_skills/skill_catalog.py +257 -0
- code_puppy_core_plugins/agent_skills/skill_commands.py +102 -0
- code_puppy_core_plugins/agent_skills/skills_install_menu.py +698 -0
- code_puppy_core_plugins/agent_skills/skills_menu.py +794 -0
- code_puppy_core_plugins/aws_bedrock/__init__.py +14 -0
- code_puppy_core_plugins/aws_bedrock/config.py +101 -0
- code_puppy_core_plugins/aws_bedrock/register_callbacks.py +243 -0
- code_puppy_core_plugins/aws_bedrock/utils.py +186 -0
- code_puppy_core_plugins/azure_foundry/README.md +238 -0
- code_puppy_core_plugins/azure_foundry/__init__.py +15 -0
- code_puppy_core_plugins/azure_foundry/config.py +126 -0
- code_puppy_core_plugins/azure_foundry/discovery.py +189 -0
- code_puppy_core_plugins/azure_foundry/register_callbacks.py +506 -0
- code_puppy_core_plugins/azure_foundry/token.py +182 -0
- code_puppy_core_plugins/azure_foundry/utils.py +375 -0
- code_puppy_core_plugins/btw/__init__.py +1 -0
- code_puppy_core_plugins/btw/inline_view.py +135 -0
- code_puppy_core_plugins/btw/register_callbacks.py +106 -0
- code_puppy_core_plugins/btw/side_query.py +91 -0
- code_puppy_core_plugins/chatgpt_oauth/IMAGEGEN_SKILL.md +83 -0
- code_puppy_core_plugins/chatgpt_oauth/__init__.py +8 -0
- code_puppy_core_plugins/chatgpt_oauth/config.py +52 -0
- code_puppy_core_plugins/chatgpt_oauth/image_generation.py +253 -0
- code_puppy_core_plugins/chatgpt_oauth/image_tool.py +70 -0
- code_puppy_core_plugins/chatgpt_oauth/oauth_flow.py +461 -0
- code_puppy_core_plugins/chatgpt_oauth/register_callbacks.py +288 -0
- code_puppy_core_plugins/chatgpt_oauth/test_plugin.py +663 -0
- code_puppy_core_plugins/chatgpt_oauth/usage.py +113 -0
- code_puppy_core_plugins/chatgpt_oauth/utils.py +534 -0
- code_puppy_core_plugins/claude_code_hooks/__init__.py +1 -0
- code_puppy_core_plugins/claude_code_hooks/config.py +137 -0
- code_puppy_core_plugins/claude_code_hooks/register_callbacks.py +383 -0
- code_puppy_core_plugins/claude_code_oauth/README.md +167 -0
- code_puppy_core_plugins/claude_code_oauth/SETUP.md +93 -0
- code_puppy_core_plugins/claude_code_oauth/__init__.py +25 -0
- code_puppy_core_plugins/claude_code_oauth/config.py +59 -0
- code_puppy_core_plugins/claude_code_oauth/fast_mode.py +122 -0
- code_puppy_core_plugins/claude_code_oauth/prompt_handler.py +64 -0
- code_puppy_core_plugins/claude_code_oauth/register_callbacks.py +719 -0
- code_puppy_core_plugins/claude_code_oauth/test_plugin.py +285 -0
- code_puppy_core_plugins/claude_code_oauth/token_refresh_heartbeat.py +240 -0
- code_puppy_core_plugins/claude_code_oauth/utils.py +663 -0
- code_puppy_core_plugins/code_puppy_agent/AGENTS_AND_TOOLS.md +155 -0
- code_puppy_core_plugins/code_puppy_agent/MODELS_AND_MCP.md +104 -0
- code_puppy_core_plugins/code_puppy_agent/PLUGINS_AND_CALLBACKS.md +147 -0
- code_puppy_core_plugins/code_puppy_agent/SESSIONS_AND_HISTORY.md +39 -0
- code_puppy_core_plugins/code_puppy_agent/SKILL.md +180 -0
- code_puppy_core_plugins/code_puppy_agent/SKILLS_SYSTEM.md +136 -0
- code_puppy_core_plugins/code_puppy_agent/SYSTEM_PROMPT_CONFIG_AND_I18N.md +160 -0
- code_puppy_core_plugins/code_puppy_agent/__init__.py +5 -0
- code_puppy_core_plugins/code_puppy_agent/register_callbacks.py +46 -0
- code_puppy_core_plugins/computer_use/NativeCapture.swift +123 -0
- code_puppy_core_plugins/computer_use/README.md +109 -0
- code_puppy_core_plugins/computer_use/__init__.py +1 -0
- code_puppy_core_plugins/computer_use/accessibility.py +163 -0
- code_puppy_core_plugins/computer_use/activation.py +75 -0
- code_puppy_core_plugins/computer_use/backend.py +584 -0
- code_puppy_core_plugins/computer_use/backend_types.py +5 -0
- code_puppy_core_plugins/computer_use/capture.py +110 -0
- code_puppy_core_plugins/computer_use/commands.py +62 -0
- code_puppy_core_plugins/computer_use/geometry.py +81 -0
- code_puppy_core_plugins/computer_use/inline_image.py +56 -0
- code_puppy_core_plugins/computer_use/keycodes.py +47 -0
- code_puppy_core_plugins/computer_use/policy.py +144 -0
- code_puppy_core_plugins/computer_use/register_callbacks.py +96 -0
- code_puppy_core_plugins/computer_use/safety.py +18 -0
- code_puppy_core_plugins/computer_use/settle.py +64 -0
- code_puppy_core_plugins/computer_use/state.py +86 -0
- code_puppy_core_plugins/computer_use/tools.py +469 -0
- code_puppy_core_plugins/context_indicator/__init__.py +14 -0
- code_puppy_core_plugins/context_indicator/register_callbacks.py +249 -0
- code_puppy_core_plugins/context_indicator/usage.py +40 -0
- code_puppy_core_plugins/copilot_auth/__init__.py +11 -0
- code_puppy_core_plugins/copilot_auth/config.py +93 -0
- code_puppy_core_plugins/copilot_auth/reasoning_client.py +409 -0
- code_puppy_core_plugins/copilot_auth/register_callbacks.py +460 -0
- code_puppy_core_plugins/copilot_auth/utils.py +587 -0
- code_puppy_core_plugins/customizable_commands/__init__.py +0 -0
- code_puppy_core_plugins/customizable_commands/register_callbacks.py +464 -0
- code_puppy_core_plugins/dbos_durable_exec/__init__.py +1 -0
- code_puppy_core_plugins/dbos_durable_exec/cancel.py +15 -0
- code_puppy_core_plugins/dbos_durable_exec/commands.py +31 -0
- code_puppy_core_plugins/dbos_durable_exec/config.py +31 -0
- code_puppy_core_plugins/dbos_durable_exec/lifecycle.py +83 -0
- code_puppy_core_plugins/dbos_durable_exec/register_callbacks.py +45 -0
- code_puppy_core_plugins/dbos_durable_exec/runtime.py +65 -0
- code_puppy_core_plugins/dbos_durable_exec/startup_lock.py +155 -0
- code_puppy_core_plugins/dbos_durable_exec/workflow_ids.py +16 -0
- code_puppy_core_plugins/dbos_durable_exec/wrapper.py +58 -0
- code_puppy_core_plugins/destructive_command_guard/AGENTS.md +79 -0
- code_puppy_core_plugins/destructive_command_guard/__init__.py +14 -0
- code_puppy_core_plugins/destructive_command_guard/detector.py +374 -0
- code_puppy_core_plugins/destructive_command_guard/register_callbacks.py +165 -0
- code_puppy_core_plugins/emoji_filter/__init__.py +12 -0
- code_puppy_core_plugins/emoji_filter/config.py +26 -0
- code_puppy_core_plugins/emoji_filter/register_callbacks.py +238 -0
- code_puppy_core_plugins/emoji_filter/stripper.py +45 -0
- code_puppy_core_plugins/empty_html_comment_filter/register_callbacks.py +116 -0
- code_puppy_core_plugins/example_custom_command/README.md +328 -0
- code_puppy_core_plugins/example_custom_command/register_callbacks.py +66 -0
- code_puppy_core_plugins/file_permission_handler/__init__.py +4 -0
- code_puppy_core_plugins/file_permission_handler/register_callbacks.py +575 -0
- code_puppy_core_plugins/flux_bootstrap/__init__.py +5 -0
- code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/_docs/README.md +173 -0
- code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/_docs/cheatsheet.md +86 -0
- code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/_docs/pipeline.md +167 -0
- code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/_docs/synopsis.md +392 -0
- code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/about.md +51 -0
- code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/address-feedback.md +129 -0
- code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/ask.md +211 -0
- code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/aug.md +181 -0
- code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/auto-pilot.md +71 -0
- code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/cheatsheet.md +16 -0
- code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/commit.md +103 -0
- code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/config.md +108 -0
- code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/create-pr.md +195 -0
- code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/exec.md +221 -0
- code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/new.md +151 -0
- code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/qa.md +208 -0
- code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/rebase.md +180 -0
- code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/review.md +304 -0
- code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/split.md +120 -0
- code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/squash-commits.md +179 -0
- code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/status.md +46 -0
- code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/tests.md +155 -0
- code_puppy_core_plugins/flux_bootstrap/bundled/scripts/flux_about.py +154 -0
- code_puppy_core_plugins/flux_bootstrap/bundled/scripts/flux_cheatsheet.py +248 -0
- code_puppy_core_plugins/flux_bootstrap/bundled/scripts/flux_status.py +345 -0
- code_puppy_core_plugins/flux_bootstrap/installer.py +256 -0
- code_puppy_core_plugins/flux_bootstrap/register_callbacks.py +71 -0
- code_puppy_core_plugins/force_push_guard/__init__.py +5 -0
- code_puppy_core_plugins/force_push_guard/detector.py +96 -0
- code_puppy_core_plugins/force_push_guard/register_callbacks.py +161 -0
- code_puppy_core_plugins/fork/__init__.py +7 -0
- code_puppy_core_plugins/fork/register_callbacks.py +445 -0
- code_puppy_core_plugins/frontend_emitter/__init__.py +25 -0
- code_puppy_core_plugins/frontend_emitter/emitter.py +249 -0
- code_puppy_core_plugins/frontend_emitter/register_callbacks.py +412 -0
- code_puppy_core_plugins/frontend_emitter/session_context.py +50 -0
- code_puppy_core_plugins/grok_oauth/__init__.py +6 -0
- code_puppy_core_plugins/grok_oauth/config.py +49 -0
- code_puppy_core_plugins/grok_oauth/oauth_flow.py +213 -0
- code_puppy_core_plugins/grok_oauth/register_callbacks.py +132 -0
- code_puppy_core_plugins/grok_oauth/utils.py +195 -0
- code_puppy_core_plugins/herdr/README.md +168 -0
- code_puppy_core_plugins/herdr/__init__.py +9 -0
- code_puppy_core_plugins/herdr/client.py +339 -0
- code_puppy_core_plugins/herdr/register_callbacks.py +140 -0
- code_puppy_core_plugins/herdr/reporter.py +212 -0
- code_puppy_core_plugins/herdr/smoke.py +143 -0
- code_puppy_core_plugins/herdr/sources.py +133 -0
- code_puppy_core_plugins/hook_creator/__init__.py +1 -0
- code_puppy_core_plugins/hook_creator/register_callbacks.py +33 -0
- code_puppy_core_plugins/hook_manager/__init__.py +1 -0
- code_puppy_core_plugins/hook_manager/config.py +290 -0
- code_puppy_core_plugins/hook_manager/hooks_menu.py +571 -0
- code_puppy_core_plugins/hook_manager/register_callbacks.py +227 -0
- code_puppy_core_plugins/meta_oauth/__init__.py +1 -0
- code_puppy_core_plugins/meta_oauth/config.py +55 -0
- code_puppy_core_plugins/meta_oauth/oauth_flow.py +109 -0
- code_puppy_core_plugins/meta_oauth/register_callbacks.py +154 -0
- code_puppy_core_plugins/meta_oauth/utils.py +252 -0
- code_puppy_core_plugins/namespace_skill_search/README.md +118 -0
- code_puppy_core_plugins/namespace_skill_search/__init__.py +15 -0
- code_puppy_core_plugins/namespace_skill_search/namespaces.py +125 -0
- code_puppy_core_plugins/namespace_skill_search/register_callbacks.py +99 -0
- code_puppy_core_plugins/namespace_skill_search/search_tool.py +158 -0
- code_puppy_core_plugins/no_tools/__init__.py +0 -0
- code_puppy_core_plugins/no_tools/register_callbacks.py +42 -0
- code_puppy_core_plugins/oauth_pasteback.py +110 -0
- code_puppy_core_plugins/oauth_puppy_html.py +194 -0
- code_puppy_core_plugins/obsidian_agent/README.md +52 -0
- code_puppy_core_plugins/obsidian_agent/__init__.py +8 -0
- code_puppy_core_plugins/obsidian_agent/agent_obsidian.py +147 -0
- code_puppy_core_plugins/obsidian_agent/register_callbacks.py +13 -0
- code_puppy_core_plugins/ollama/__init__.py +1 -0
- code_puppy_core_plugins/ollama/register_callbacks.py +127 -0
- code_puppy_core_plugins/ollama_setup/__init__.py +5 -0
- code_puppy_core_plugins/ollama_setup/completer.py +38 -0
- code_puppy_core_plugins/ollama_setup/register_callbacks.py +419 -0
- code_puppy_core_plugins/plugin_list/__init__.py +0 -0
- code_puppy_core_plugins/plugin_list/plugin_contributions.py +348 -0
- code_puppy_core_plugins/plugin_list/plugin_meta.py +67 -0
- code_puppy_core_plugins/plugin_list/plugin_text_utils.py +173 -0
- code_puppy_core_plugins/plugin_list/plugins_menu.py +455 -0
- code_puppy_core_plugins/plugin_list/plugins_menu_layout.py +194 -0
- code_puppy_core_plugins/plugin_list/plugins_menu_render.py +431 -0
- code_puppy_core_plugins/plugin_list/project_trust_flow.py +162 -0
- code_puppy_core_plugins/plugin_list/register_callbacks.py +281 -0
- code_puppy_core_plugins/pop_command/__init__.py +1 -0
- code_puppy_core_plugins/pop_command/register_callbacks.py +191 -0
- code_puppy_core_plugins/prompt_newline/__init__.py +13 -0
- code_puppy_core_plugins/prompt_newline/config.py +21 -0
- code_puppy_core_plugins/prompt_newline/register_callbacks.py +163 -0
- code_puppy_core_plugins/prune/__init__.py +1 -0
- code_puppy_core_plugins/prune/prune_menu.py +308 -0
- code_puppy_core_plugins/prune/prune_model.py +517 -0
- code_puppy_core_plugins/prune/prune_render.py +354 -0
- code_puppy_core_plugins/prune/register_callbacks.py +388 -0
- code_puppy_core_plugins/puppy_kennel/README.md +203 -0
- code_puppy_core_plugins/puppy_kennel/__init__.py +6 -0
- code_puppy_core_plugins/puppy_kennel/commands.py +206 -0
- code_puppy_core_plugins/puppy_kennel/config.py +36 -0
- code_puppy_core_plugins/puppy_kennel/kennel.py +368 -0
- code_puppy_core_plugins/puppy_kennel/packer.py +152 -0
- code_puppy_core_plugins/puppy_kennel/recorder.py +83 -0
- code_puppy_core_plugins/puppy_kennel/register_callbacks.py +109 -0
- code_puppy_core_plugins/puppy_kennel/retriever.py +28 -0
- code_puppy_core_plugins/puppy_kennel/schema.py +78 -0
- code_puppy_core_plugins/puppy_kennel/state.py +68 -0
- code_puppy_core_plugins/puppy_kennel/tools.py +449 -0
- code_puppy_core_plugins/puppy_kennel/wings.py +110 -0
- code_puppy_core_plugins/puppy_spinner/README.md +93 -0
- code_puppy_core_plugins/puppy_spinner/__init__.py +11 -0
- code_puppy_core_plugins/puppy_spinner/builtin_frames.py +172 -0
- code_puppy_core_plugins/puppy_spinner/commands.py +116 -0
- code_puppy_core_plugins/puppy_spinner/picker.py +379 -0
- code_puppy_core_plugins/puppy_spinner/register_callbacks.py +193 -0
- code_puppy_core_plugins/puppy_spinner/spinners.py +444 -0
- code_puppy_core_plugins/quick_resume/__init__.py +0 -0
- code_puppy_core_plugins/quick_resume/register_callbacks.py +77 -0
- code_puppy_core_plugins/review_pr/__init__.py +1 -0
- code_puppy_core_plugins/review_pr/register_callbacks.py +162 -0
- code_puppy_core_plugins/shell_safety/__init__.py +6 -0
- code_puppy_core_plugins/shell_safety/agent_shell_safety.py +69 -0
- code_puppy_core_plugins/shell_safety/command_cache.py +156 -0
- code_puppy_core_plugins/shell_safety/register_callbacks.py +203 -0
- code_puppy_core_plugins/stack_dump/__init__.py +1 -0
- code_puppy_core_plugins/stack_dump/register_callbacks.py +79 -0
- code_puppy_core_plugins/statusline/__init__.py +14 -0
- code_puppy_core_plugins/statusline/config.py +68 -0
- code_puppy_core_plugins/statusline/payload.py +138 -0
- code_puppy_core_plugins/statusline/prompt_patch.py +76 -0
- code_puppy_core_plugins/statusline/register_callbacks.py +45 -0
- code_puppy_core_plugins/statusline/runner.py +122 -0
- code_puppy_core_plugins/statusline/statusline_command.py +197 -0
- code_puppy_core_plugins/steer_queue/__init__.py +10 -0
- code_puppy_core_plugins/steer_queue/queue_menu.py +444 -0
- code_puppy_core_plugins/steer_queue/register_callbacks.py +138 -0
- code_puppy_core_plugins/subagent_panel/README.md +102 -0
- code_puppy_core_plugins/subagent_panel/__init__.py +24 -0
- code_puppy_core_plugins/subagent_panel/coalesce_patch.py +242 -0
- code_puppy_core_plugins/subagent_panel/panel_render.py +145 -0
- code_puppy_core_plugins/subagent_panel/register_callbacks.py +535 -0
- code_puppy_core_plugins/subagent_panel/resume_repaint.py +112 -0
- code_puppy_core_plugins/subagent_panel/state.py +231 -0
- code_puppy_core_plugins/switch_agent_resume/__init__.py +5 -0
- code_puppy_core_plugins/switch_agent_resume/register_callbacks.py +314 -0
- code_puppy_core_plugins/theme/README.md +86 -0
- code_puppy_core_plugins/theme/__init__.py +14 -0
- code_puppy_core_plugins/theme/bundled_palettes.py +346 -0
- code_puppy_core_plugins/theme/content_styles.py +146 -0
- code_puppy_core_plugins/theme/osc_palette.py +178 -0
- code_puppy_core_plugins/theme/picker.py +358 -0
- code_puppy_core_plugins/theme/prompt_toolkit_theme.py +113 -0
- code_puppy_core_plugins/theme/register_callbacks.py +348 -0
- code_puppy_core_plugins/theme/rich_themes.py +202 -0
- code_puppy_core_plugins/theme/themes.py +720 -0
- code_puppy_core_plugins/timestamp_heartbeat/__init__.py +1 -0
- code_puppy_core_plugins/timestamp_heartbeat/register_callbacks.py +114 -0
- code_puppy_core_plugins/token_ratio_learner/__init__.py +5 -0
- code_puppy_core_plugins/token_ratio_learner/ratios.py +208 -0
- code_puppy_core_plugins/token_ratio_learner/register_callbacks.py +213 -0
- code_puppy_core_plugins/universal_constructor/__init__.py +13 -0
- code_puppy_core_plugins/universal_constructor/models.py +138 -0
- code_puppy_core_plugins/universal_constructor/provider.py +62 -0
- code_puppy_core_plugins/universal_constructor/register_callbacks.py +75 -0
- code_puppy_core_plugins/universal_constructor/registry.py +302 -0
- code_puppy_core_plugins/universal_constructor/sandbox.py +584 -0
- code_puppy_core_plugins/wide_completion_menu/__init__.py +6 -0
- code_puppy_core_plugins/wide_completion_menu/register_callbacks.py +165 -0
- code_puppy_core_plugins/wiggum/__init__.py +1 -0
- code_puppy_core_plugins/wiggum/goal_runs.py +91 -0
- code_puppy_core_plugins/wiggum/judge.py +299 -0
- code_puppy_core_plugins/wiggum/judge_config.py +249 -0
- code_puppy_core_plugins/wiggum/judges_menu.py +814 -0
- code_puppy_core_plugins/wiggum/register_callbacks.py +569 -0
- code_puppy_core_plugins/wiggum/state.py +89 -0
- code_puppy_core_plugins/yolo_cli/__init__.py +1 -0
- code_puppy_core_plugins/yolo_cli/register_callbacks.py +41 -0
- code_puppy_core_plugins-0.0.2.dist-info/METADATA +64 -0
- code_puppy_core_plugins-0.0.2.dist-info/RECORD +313 -0
- code_puppy_core_plugins-0.0.2.dist-info/WHEEL +4 -0
- code_puppy_core_plugins-0.0.2.dist-info/entry_points.txt +55 -0
- code_puppy_core_plugins-0.0.2.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
"""``/plugins`` slash command -- manage plugins interactively or via subcommands.
|
|
2
|
+
|
|
3
|
+
Usage::
|
|
4
|
+
|
|
5
|
+
/plugins -- open interactive TUI
|
|
6
|
+
/plugins list -- print loaded plugins with status
|
|
7
|
+
/plugins disable <name> -- disable a plugin (callbacks are skipped)
|
|
8
|
+
/plugins enable <name> -- enable a plugin; untrusted project plugins
|
|
9
|
+
open the TUI trust ceremony directly
|
|
10
|
+
/plugins revoke <name> -- revoke trust for a project plugin
|
|
11
|
+
|
|
12
|
+
Dogfoods the plugin system by implementing itself as a builtin plugin that
|
|
13
|
+
hooks into ``custom_command`` and ``custom_command_help``.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import logging
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
from typing import Optional
|
|
21
|
+
|
|
22
|
+
from code_puppy.callbacks import register_callback
|
|
23
|
+
|
|
24
|
+
logger = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _format_plugin_list(names: list[str], disabled: set[str]) -> str:
|
|
28
|
+
"""Return a bullet list of plugin names with status indicators."""
|
|
29
|
+
if not names:
|
|
30
|
+
return " (none)"
|
|
31
|
+
lines = []
|
|
32
|
+
for name in sorted(names):
|
|
33
|
+
if name in disabled:
|
|
34
|
+
lines.append(f" {name} (disabled)")
|
|
35
|
+
else:
|
|
36
|
+
lines.append(f" {name}")
|
|
37
|
+
return "\n".join(lines)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
_PROJECT_STATUS_LABELS = {
|
|
41
|
+
"untrusted": "(not enabled -- review & enable in the /plugins TUI)",
|
|
42
|
+
"changed": "(changed since accepted -- re-review in the /plugins TUI)",
|
|
43
|
+
"disabled": "(disabled)",
|
|
44
|
+
"error": "(failed to load -- see logs)",
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _format_project_plugin_list(
|
|
49
|
+
loaded_names: list[str],
|
|
50
|
+
statuses: dict[str, str],
|
|
51
|
+
disabled: set[str],
|
|
52
|
+
) -> str:
|
|
53
|
+
"""Project tier listing: loaded plugins plus skipped-by-trust ones.
|
|
54
|
+
|
|
55
|
+
Project plugins are disabled by default; unlike other tiers we also
|
|
56
|
+
show plugins that were discovered but NOT imported, with the reason.
|
|
57
|
+
"""
|
|
58
|
+
names = sorted(set(loaded_names) | set(statuses))
|
|
59
|
+
if not names:
|
|
60
|
+
return " (none)"
|
|
61
|
+
lines = []
|
|
62
|
+
for name in names:
|
|
63
|
+
status = statuses.get(name, "loaded" if name in loaded_names else "untrusted")
|
|
64
|
+
if status == "loaded":
|
|
65
|
+
suffix = " (disabled)" if name in disabled else ""
|
|
66
|
+
else:
|
|
67
|
+
suffix = " " + _PROJECT_STATUS_LABELS.get(status, f"({status})")
|
|
68
|
+
lines.append(f" {name}{suffix}")
|
|
69
|
+
return "\n".join(lines)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _build_output() -> str:
|
|
73
|
+
"""Build the full /plugins list display string."""
|
|
74
|
+
from code_puppy.plugins import (
|
|
75
|
+
get_loaded_plugins,
|
|
76
|
+
get_project_plugins_directory,
|
|
77
|
+
get_project_plugin_status,
|
|
78
|
+
)
|
|
79
|
+
from code_puppy.plugins.config import get_disabled_plugins
|
|
80
|
+
|
|
81
|
+
loaded = get_loaded_plugins()
|
|
82
|
+
disabled = get_disabled_plugins()
|
|
83
|
+
project_status = get_project_plugin_status()
|
|
84
|
+
|
|
85
|
+
# Display paths with forward slashes regardless of OS so the output is
|
|
86
|
+
# consistent across Windows / POSIX (and easier to copy-paste).
|
|
87
|
+
builtin_path = Path(__file__).parent.parent.as_posix() + "/"
|
|
88
|
+
user_path = "~/.code_puppy/plugins/"
|
|
89
|
+
project_dir = get_project_plugins_directory()
|
|
90
|
+
project_path = (
|
|
91
|
+
project_dir.as_posix() + "/" if project_dir else "<CWD>/.code_puppy/plugins/"
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
lines = [
|
|
95
|
+
"Loaded Plugins",
|
|
96
|
+
"",
|
|
97
|
+
f"Builtin ({builtin_path}):",
|
|
98
|
+
_format_plugin_list(loaded["builtin"], disabled),
|
|
99
|
+
"",
|
|
100
|
+
f"User ({user_path}):",
|
|
101
|
+
_format_plugin_list(loaded["user"], disabled),
|
|
102
|
+
"",
|
|
103
|
+
f"Project ({project_path}):",
|
|
104
|
+
_format_project_plugin_list(loaded["project"], project_status, disabled),
|
|
105
|
+
]
|
|
106
|
+
|
|
107
|
+
if disabled:
|
|
108
|
+
lines.extend(
|
|
109
|
+
[
|
|
110
|
+
"",
|
|
111
|
+
f"Disabled: {', '.join(sorted(disabled))}",
|
|
112
|
+
"Re-enable it in the /plugins TUI.",
|
|
113
|
+
]
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
return "\n".join(lines)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def _all_loaded_plugin_names() -> set[str]:
|
|
120
|
+
"""Return the set of all loaded plugin names across all tiers."""
|
|
121
|
+
from code_puppy.plugins import get_loaded_plugins
|
|
122
|
+
|
|
123
|
+
loaded = get_loaded_plugins()
|
|
124
|
+
names: set[str] = set()
|
|
125
|
+
for tier_names in loaded.values():
|
|
126
|
+
names.update(tier_names)
|
|
127
|
+
return names
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _handle_toggle(plugin_name: str, *, disabled: bool) -> bool:
|
|
131
|
+
"""Flip *plugin_name*'s disabled state to *disabled*.
|
|
132
|
+
|
|
133
|
+
One implementation handles both ``enable`` and ``disable`` — the only
|
|
134
|
+
difference is the target state and the success-message verb.
|
|
135
|
+
"""
|
|
136
|
+
from code_puppy.messaging import emit_error, emit_info, emit_success, emit_warning
|
|
137
|
+
from code_puppy.plugins.config import set_plugin_disabled
|
|
138
|
+
|
|
139
|
+
if plugin_name not in _all_loaded_plugin_names():
|
|
140
|
+
emit_error(
|
|
141
|
+
f"Plugin '{plugin_name}' is not loaded. "
|
|
142
|
+
f"Use /plugins to see available plugins."
|
|
143
|
+
)
|
|
144
|
+
return True
|
|
145
|
+
|
|
146
|
+
verb = "disabled" if disabled else "re-enabled"
|
|
147
|
+
state = "disabled" if disabled else "enabled"
|
|
148
|
+
if set_plugin_disabled(plugin_name, disabled):
|
|
149
|
+
emit_success(f"Plugin '{plugin_name}' {verb}.")
|
|
150
|
+
emit_warning("Restart Code Puppy for this change to take effect.")
|
|
151
|
+
else:
|
|
152
|
+
emit_info(f"Plugin '{plugin_name}' is already {state}.")
|
|
153
|
+
return True
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
# -- startup hook ----------------------------------------------------------
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def _on_startup() -> None:
|
|
160
|
+
"""Surface project plugins held back by the trust gate.
|
|
161
|
+
|
|
162
|
+
Runs via the ``startup`` callback, which fires after the renderers are
|
|
163
|
+
live — so the orange banner lands inline with the other startup
|
|
164
|
+
messages instead of rotting in the legacy queue's startup buffer
|
|
165
|
+
(which SynchronousInteractiveRenderer never replays).
|
|
166
|
+
"""
|
|
167
|
+
try:
|
|
168
|
+
from code_puppy.plugins import get_project_plugin_status
|
|
169
|
+
from code_puppy.plugins.trust_notice import emit_skipped_plugin_notice
|
|
170
|
+
|
|
171
|
+
emit_skipped_plugin_notice(get_project_plugin_status())
|
|
172
|
+
except Exception as exc:
|
|
173
|
+
logger.debug(f"Trust-notice startup hook failed: {exc}")
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
# -- custom_command hooks --------------------------------------------------
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _custom_help() -> list[tuple[str, str]]:
|
|
180
|
+
return [("plugins", "List, enable/disable, or trust project plugins")]
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def _run_interactive_menu() -> None:
|
|
184
|
+
"""Open the TUI; fall back to a plain-text list if it blows up."""
|
|
185
|
+
from code_puppy.messaging import emit_info
|
|
186
|
+
|
|
187
|
+
try:
|
|
188
|
+
from code_puppy_core_plugins.plugin_list.plugins_menu import run_plugins_menu
|
|
189
|
+
|
|
190
|
+
run_plugins_menu()
|
|
191
|
+
except Exception as exc:
|
|
192
|
+
logger.warning(f"Plugins TUI failed, falling back to list: {exc}")
|
|
193
|
+
emit_info(_build_output())
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def _sub_list(_tokens: list[str]) -> bool:
|
|
197
|
+
from code_puppy.messaging import emit_info
|
|
198
|
+
|
|
199
|
+
emit_info(_build_output())
|
|
200
|
+
return True
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def _sub_disable(tokens: list[str]) -> bool:
|
|
204
|
+
return _sub_toggle(tokens, disabled=True)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def _sub_enable(tokens: list[str]) -> bool:
|
|
208
|
+
from code_puppy.messaging import emit_error
|
|
209
|
+
|
|
210
|
+
if len(tokens) < 3:
|
|
211
|
+
emit_error("Usage: /plugins enable <plugin-name>")
|
|
212
|
+
return True
|
|
213
|
+
|
|
214
|
+
# Project plugins get the trust/risk-acceptance flow; anything else
|
|
215
|
+
# falls through to the regular enable toggle.
|
|
216
|
+
from code_puppy_core_plugins.plugin_list.project_trust_flow import (
|
|
217
|
+
try_enable_project_plugin,
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
if try_enable_project_plugin(tokens[2]):
|
|
221
|
+
return True
|
|
222
|
+
return _handle_toggle(tokens[2], disabled=False)
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def _sub_revoke(tokens: list[str]) -> bool:
|
|
226
|
+
from code_puppy.messaging import emit_error
|
|
227
|
+
|
|
228
|
+
if len(tokens) < 3:
|
|
229
|
+
emit_error("Usage: /plugins revoke <plugin-name>")
|
|
230
|
+
return True
|
|
231
|
+
|
|
232
|
+
from code_puppy_core_plugins.plugin_list.project_trust_flow import (
|
|
233
|
+
revoke_project_plugin,
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
return revoke_project_plugin(tokens[2])
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def _sub_toggle(tokens: list[str], *, disabled: bool) -> bool:
|
|
240
|
+
from code_puppy.messaging import emit_error
|
|
241
|
+
|
|
242
|
+
action = "disable" if disabled else "enable"
|
|
243
|
+
if len(tokens) < 3:
|
|
244
|
+
emit_error(f"Usage: /plugins {action} <plugin-name>")
|
|
245
|
+
return True
|
|
246
|
+
return _handle_toggle(tokens[2], disabled=disabled)
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
_SUBCOMMANDS = {
|
|
250
|
+
"list": _sub_list,
|
|
251
|
+
"disable": _sub_disable,
|
|
252
|
+
"enable": _sub_enable,
|
|
253
|
+
"revoke": _sub_revoke,
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def _handle_custom_command(command: str, name: str) -> Optional[bool]:
|
|
258
|
+
if name != "plugins":
|
|
259
|
+
return None
|
|
260
|
+
|
|
261
|
+
tokens = command.strip().split()
|
|
262
|
+
|
|
263
|
+
if len(tokens) <= 1:
|
|
264
|
+
_run_interactive_menu()
|
|
265
|
+
return True
|
|
266
|
+
|
|
267
|
+
handler = _SUBCOMMANDS.get(tokens[1].lower())
|
|
268
|
+
if handler is None:
|
|
269
|
+
from code_puppy.messaging import emit_error
|
|
270
|
+
|
|
271
|
+
emit_error(
|
|
272
|
+
f"Unknown subcommand: '{tokens[1]}'. "
|
|
273
|
+
"Usage: /plugins [list | enable <name> | disable <name> | revoke <name>]"
|
|
274
|
+
)
|
|
275
|
+
return True
|
|
276
|
+
return handler(tokens)
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
register_callback("startup", _on_startup)
|
|
280
|
+
register_callback("custom_command_help", _custom_help)
|
|
281
|
+
register_callback("custom_command", _handle_custom_command)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""/pop command plugin."""
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"""Plugin that adds /pop for trimming recent conversation history."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, List, Optional, Tuple
|
|
6
|
+
|
|
7
|
+
from code_puppy.callbacks import register_callback
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def emit_error(message: Any) -> None:
|
|
11
|
+
from code_puppy.messaging import emit_error as _emit_error
|
|
12
|
+
|
|
13
|
+
_emit_error(message)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def emit_info(message: Any) -> None:
|
|
17
|
+
from code_puppy.messaging import emit_info as _emit_info
|
|
18
|
+
|
|
19
|
+
_emit_info(message)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def emit_success(message: Any) -> None:
|
|
23
|
+
from code_puppy.messaging import emit_success as _emit_success
|
|
24
|
+
|
|
25
|
+
_emit_success(message)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def emit_warning(message: Any) -> None:
|
|
29
|
+
from code_puppy.messaging import emit_warning as _emit_warning
|
|
30
|
+
|
|
31
|
+
_emit_warning(message)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _custom_help() -> List[Tuple[str, str]]:
|
|
35
|
+
return [
|
|
36
|
+
(
|
|
37
|
+
"pop",
|
|
38
|
+
"Delete the N most-recent messages and prune broken tool-call fragments",
|
|
39
|
+
)
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _has_only_tool_returns(message: Any) -> bool:
|
|
44
|
+
"""Return True when a request message contains only tool-return parts."""
|
|
45
|
+
try:
|
|
46
|
+
from pydantic_ai.messages import ModelRequest, ToolReturnPart
|
|
47
|
+
|
|
48
|
+
if not isinstance(message, ModelRequest):
|
|
49
|
+
return False
|
|
50
|
+
|
|
51
|
+
parts = getattr(message, "parts", []) or []
|
|
52
|
+
return bool(parts) and all(isinstance(part, ToolReturnPart) for part in parts)
|
|
53
|
+
except Exception:
|
|
54
|
+
return False
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _has_unresolved_tool_calls(message: Any) -> bool:
|
|
58
|
+
"""Return True when a response message still contains unresolved tool calls."""
|
|
59
|
+
try:
|
|
60
|
+
from pydantic_ai.messages import ModelResponse, ToolCallPart
|
|
61
|
+
|
|
62
|
+
if not isinstance(message, ModelResponse):
|
|
63
|
+
return False
|
|
64
|
+
|
|
65
|
+
parts = getattr(message, "parts", []) or []
|
|
66
|
+
return any(isinstance(part, ToolCallPart) for part in parts)
|
|
67
|
+
except Exception:
|
|
68
|
+
return False
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _prune_dangling_tool_fragments(history: List[Any]) -> tuple[List[Any], int]:
|
|
72
|
+
"""Strip incomplete tool-call sequences from the tail of history."""
|
|
73
|
+
pruned = 0
|
|
74
|
+
|
|
75
|
+
while history:
|
|
76
|
+
tail = history[-1]
|
|
77
|
+
if _has_only_tool_returns(tail):
|
|
78
|
+
history.pop()
|
|
79
|
+
pruned += 1
|
|
80
|
+
continue
|
|
81
|
+
|
|
82
|
+
if _has_unresolved_tool_calls(tail):
|
|
83
|
+
history.pop()
|
|
84
|
+
pruned += 1
|
|
85
|
+
continue
|
|
86
|
+
|
|
87
|
+
break
|
|
88
|
+
|
|
89
|
+
return history, pruned
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _parse_pop_count(command: str) -> Optional[int]:
|
|
93
|
+
tokens = command.split()
|
|
94
|
+
if len(tokens) < 2:
|
|
95
|
+
return 1
|
|
96
|
+
|
|
97
|
+
try:
|
|
98
|
+
count = int(tokens[1])
|
|
99
|
+
except ValueError:
|
|
100
|
+
emit_error(f"/pop: '{tokens[1]}' is not a valid integer – usage: /pop [N]")
|
|
101
|
+
return None
|
|
102
|
+
|
|
103
|
+
if count < 1:
|
|
104
|
+
emit_error("/pop: N must be a positive integer")
|
|
105
|
+
return None
|
|
106
|
+
|
|
107
|
+
return count
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _handle_pop_command(command: str) -> bool:
|
|
111
|
+
from code_puppy.agents.agent_manager import get_current_agent
|
|
112
|
+
|
|
113
|
+
count = _parse_pop_count(command)
|
|
114
|
+
if count is None:
|
|
115
|
+
return True
|
|
116
|
+
|
|
117
|
+
try:
|
|
118
|
+
agent = get_current_agent()
|
|
119
|
+
except Exception as exc:
|
|
120
|
+
emit_error(f"/pop: could not get current agent – {exc}")
|
|
121
|
+
return True
|
|
122
|
+
|
|
123
|
+
history: List[Any] = list(agent.get_message_history())
|
|
124
|
+
if not history:
|
|
125
|
+
emit_warning("/pop: conversation history is empty – nothing to remove")
|
|
126
|
+
return True
|
|
127
|
+
|
|
128
|
+
poppable = len(history) - 1
|
|
129
|
+
if poppable <= 0:
|
|
130
|
+
emit_warning("/pop: only the system prompt is in history – nothing to remove")
|
|
131
|
+
return True
|
|
132
|
+
|
|
133
|
+
if count > poppable:
|
|
134
|
+
emit_warning(
|
|
135
|
+
f"/pop: requested {count} but only {poppable} message(s) can be removed "
|
|
136
|
+
f"(the system prompt is always preserved). Removing {poppable}."
|
|
137
|
+
)
|
|
138
|
+
count = poppable
|
|
139
|
+
|
|
140
|
+
before_count = len(history)
|
|
141
|
+
history = history[: before_count - count]
|
|
142
|
+
history, extra_pruned = _prune_dangling_tool_fragments(history)
|
|
143
|
+
after_count = len(history)
|
|
144
|
+
total_removed = before_count - after_count
|
|
145
|
+
|
|
146
|
+
try:
|
|
147
|
+
agent.set_message_history(history)
|
|
148
|
+
except Exception as exc:
|
|
149
|
+
emit_error(f"/pop: failed to update message history – {exc}")
|
|
150
|
+
return True
|
|
151
|
+
|
|
152
|
+
summary_parts = [f":scissors: Popped {count} message(s)"]
|
|
153
|
+
if extra_pruned:
|
|
154
|
+
summary_parts.append(
|
|
155
|
+
f"and pruned {extra_pruned} extra incomplete tool-call fragment(s)"
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
remaining = max(after_count - 1, 0)
|
|
159
|
+
emit_success(
|
|
160
|
+
" ".join(summary_parts)
|
|
161
|
+
+ ".\n"
|
|
162
|
+
+ f":scroll: History: {before_count - 1} → {remaining} message(s) "
|
|
163
|
+
+ f"(excluding system prompt, removed {total_removed} total)"
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
if after_count <= 1:
|
|
167
|
+
emit_info(":bulb: History is now empty (system prompt only). Starting fresh!")
|
|
168
|
+
|
|
169
|
+
return True
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def _handle_custom_command(command: str, name: str) -> Optional[bool]:
|
|
173
|
+
if name != "pop":
|
|
174
|
+
return None
|
|
175
|
+
|
|
176
|
+
return _handle_pop_command(command)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
register_callback("custom_command_help", _custom_help)
|
|
180
|
+
register_callback("custom_command", _handle_custom_command)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
__all__ = [
|
|
184
|
+
"_custom_help",
|
|
185
|
+
"_handle_custom_command",
|
|
186
|
+
"_handle_pop_command",
|
|
187
|
+
"_parse_pop_count",
|
|
188
|
+
"_prune_dangling_tool_fragments",
|
|
189
|
+
"_has_only_tool_returns",
|
|
190
|
+
"_has_unresolved_tool_calls",
|
|
191
|
+
]
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Plugin: drop user input onto a fresh line below the prompt chrome.
|
|
2
|
+
|
|
3
|
+
When enabled, transforms
|
|
4
|
+
|
|
5
|
+
🐶 puppy [agent] [model] (~/very/long/cwd) >>> typed text
|
|
6
|
+
|
|
7
|
+
into
|
|
8
|
+
|
|
9
|
+
🐶 puppy [agent] [model] (~/very/long/cwd) >>>
|
|
10
|
+
typed text
|
|
11
|
+
|
|
12
|
+
Toggle at runtime with ``/prompt_newline [on|off]``. Persisted in puppy.cfg.
|
|
13
|
+
"""
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Plugin-local config for the prompt_newline plugin."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from code_puppy.config import get_value, set_config_value
|
|
6
|
+
|
|
7
|
+
_CONFIG_KEY = "prompt_newline"
|
|
8
|
+
_TRUTHY = ("true", "1", "yes", "on")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def is_enabled() -> bool:
|
|
12
|
+
"""Return True if the prompt_newline hack is enabled. Default: False."""
|
|
13
|
+
cfg_val = get_value(_CONFIG_KEY)
|
|
14
|
+
if cfg_val is None:
|
|
15
|
+
return False
|
|
16
|
+
return str(cfg_val).strip().lower() in _TRUTHY
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def set_enabled(enabled: bool) -> None:
|
|
20
|
+
"""Persist the on/off switch to puppy.cfg."""
|
|
21
|
+
set_config_value(_CONFIG_KEY, "true" if enabled else "false")
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"""Register callbacks for the prompt_newline plugin.
|
|
2
|
+
|
|
3
|
+
This plugin is a tiny ergonomics hack: it places the user's input cursor on a
|
|
4
|
+
*new line* below the puppy/agent/model/cwd chrome, so long working-directory
|
|
5
|
+
paths don't squeeze the typing area.
|
|
6
|
+
|
|
7
|
+
It hooks two things:
|
|
8
|
+
|
|
9
|
+
* ``startup`` — wraps ``get_prompt_with_active_model`` so the FormattedText it
|
|
10
|
+
returns gets a trailing ``\\n`` appended **at call time** (so the slash
|
|
11
|
+
command toggle takes effect immediately, no restart needed).
|
|
12
|
+
* ``custom_command`` / ``custom_command_help`` — exposes ``/prompt_newline``
|
|
13
|
+
for runtime on/off, persisted via ``puppy.cfg``.
|
|
14
|
+
|
|
15
|
+
Default: OFF. Opt-in only.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
from typing import List, Optional, Tuple
|
|
21
|
+
|
|
22
|
+
from code_puppy.callbacks import register_callback
|
|
23
|
+
from code_puppy_core_plugins.prompt_newline.config import is_enabled, set_enabled
|
|
24
|
+
|
|
25
|
+
_COMMAND_NAME = "prompt_newline"
|
|
26
|
+
_PATCH_ATTR = "_prompt_newline_original"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _emit_info(message: str) -> None:
|
|
30
|
+
from code_puppy.messaging import emit_info
|
|
31
|
+
|
|
32
|
+
emit_info(message)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _emit_error(message: str) -> None:
|
|
36
|
+
from code_puppy.messaging import emit_error
|
|
37
|
+
|
|
38
|
+
emit_error(message)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _emit_success(message: str) -> None:
|
|
42
|
+
from code_puppy.messaging import emit_success
|
|
43
|
+
|
|
44
|
+
emit_success(message)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _append_newline(formatted_text):
|
|
48
|
+
"""Return a new FormattedText with a trailing newline tuple.
|
|
49
|
+
|
|
50
|
+
``FormattedText`` is a ``list`` subclass of ``(style, text)`` tuples, so we
|
|
51
|
+
rebuild it rather than mutating in place — the upstream caller may cache
|
|
52
|
+
or reuse the returned object.
|
|
53
|
+
"""
|
|
54
|
+
from prompt_toolkit.formatted_text import FormattedText
|
|
55
|
+
|
|
56
|
+
try:
|
|
57
|
+
return FormattedText(list(formatted_text) + [("", "\n")])
|
|
58
|
+
except Exception:
|
|
59
|
+
# Defensive: never break the prompt if the upstream shape changes.
|
|
60
|
+
return formatted_text
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _install_prompt_patch() -> None:
|
|
64
|
+
"""Monkey-patch ``get_prompt_with_active_model`` to honor ``is_enabled()``.
|
|
65
|
+
|
|
66
|
+
Idempotent: re-running won't double-wrap.
|
|
67
|
+
"""
|
|
68
|
+
from code_puppy.command_line import prompt_toolkit_completion as ptc
|
|
69
|
+
|
|
70
|
+
if getattr(ptc, _PATCH_ATTR, None) is not None:
|
|
71
|
+
return # Already patched
|
|
72
|
+
|
|
73
|
+
original = ptc.get_prompt_with_active_model
|
|
74
|
+
setattr(ptc, _PATCH_ATTR, original)
|
|
75
|
+
|
|
76
|
+
def patched(base: str = ">>> "):
|
|
77
|
+
result = original(base)
|
|
78
|
+
if is_enabled():
|
|
79
|
+
return _append_newline(result)
|
|
80
|
+
return result
|
|
81
|
+
|
|
82
|
+
ptc.get_prompt_with_active_model = patched
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _on_startup() -> None:
|
|
86
|
+
try:
|
|
87
|
+
_install_prompt_patch()
|
|
88
|
+
except Exception as exc:
|
|
89
|
+
# Plugins must fail gracefully — never crash the app.
|
|
90
|
+
_emit_error(f"prompt_newline: failed to install prompt patch — {exc}")
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _custom_help() -> List[Tuple[str, str]]:
|
|
94
|
+
return [
|
|
95
|
+
(
|
|
96
|
+
_COMMAND_NAME,
|
|
97
|
+
"Toggle placing user input on a new line below the prompt chrome",
|
|
98
|
+
)
|
|
99
|
+
]
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _parse_toggle_arg(command: str) -> Optional[bool]:
|
|
103
|
+
"""Parse ``/prompt_newline [on|off|true|false|toggle]``.
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
True/False for an explicit set, or ``None`` to mean "flip current".
|
|
107
|
+
"""
|
|
108
|
+
tokens = command.strip().split()
|
|
109
|
+
if len(tokens) < 2:
|
|
110
|
+
return None # bare /prompt_newline → flip
|
|
111
|
+
arg = tokens[1].lower()
|
|
112
|
+
if arg in ("on", "true", "1", "yes", "enable", "enabled"):
|
|
113
|
+
return True
|
|
114
|
+
if arg in ("off", "false", "0", "no", "disable", "disabled"):
|
|
115
|
+
return False
|
|
116
|
+
if arg in ("toggle",):
|
|
117
|
+
return None
|
|
118
|
+
raise ValueError(arg)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _handle_prompt_newline_command(command: str) -> bool:
|
|
122
|
+
try:
|
|
123
|
+
target = _parse_toggle_arg(command)
|
|
124
|
+
except ValueError as exc:
|
|
125
|
+
_emit_error(
|
|
126
|
+
f"/{_COMMAND_NAME}: unknown argument '{exc.args[0]}'. "
|
|
127
|
+
"Usage: /prompt_newline [on|off|toggle]"
|
|
128
|
+
)
|
|
129
|
+
return True
|
|
130
|
+
|
|
131
|
+
if target is None:
|
|
132
|
+
target = not is_enabled()
|
|
133
|
+
|
|
134
|
+
set_enabled(target)
|
|
135
|
+
state = "ON" if target else "OFF"
|
|
136
|
+
_emit_success(f"🐶 prompt_newline is now {state}")
|
|
137
|
+
if target:
|
|
138
|
+
_emit_info("Your input will appear on a fresh line below the prompt chrome.")
|
|
139
|
+
else:
|
|
140
|
+
_emit_info("Prompt is back to single-line mode.")
|
|
141
|
+
return True
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def _handle_custom_command(command: str, name: str):
|
|
145
|
+
if name != _COMMAND_NAME:
|
|
146
|
+
return None
|
|
147
|
+
return _handle_prompt_newline_command(command)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
register_callback("startup", _on_startup)
|
|
151
|
+
register_callback("custom_command", _handle_custom_command)
|
|
152
|
+
register_callback("custom_command_help", _custom_help)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
__all__ = [
|
|
156
|
+
"_append_newline",
|
|
157
|
+
"_custom_help",
|
|
158
|
+
"_handle_custom_command",
|
|
159
|
+
"_handle_prompt_newline_command",
|
|
160
|
+
"_install_prompt_patch",
|
|
161
|
+
"_on_startup",
|
|
162
|
+
"_parse_toggle_arg",
|
|
163
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""/prune surgical history pruner plugin."""
|