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,569 @@
|
|
|
1
|
+
"""Register the Wiggum looping slash commands and goal continuation policy."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from code_puppy.callbacks import register_callback
|
|
9
|
+
from code_puppy.command_line.command_registry import register_command
|
|
10
|
+
from code_puppy.config import get_value
|
|
11
|
+
from code_puppy.messaging import (
|
|
12
|
+
emit_info,
|
|
13
|
+
emit_success,
|
|
14
|
+
emit_system_message,
|
|
15
|
+
emit_warning,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
from . import goal_runs, state
|
|
19
|
+
from .judge import GoalJudgement, judge_goal
|
|
20
|
+
from .judge_config import JudgeConfig, get_enabled_judges_or_default, load_judges
|
|
21
|
+
|
|
22
|
+
# /goal iterations default to 10; /set goal_max_iterations overrides, clamped to
|
|
23
|
+
# [1, 1000].
|
|
24
|
+
GOAL_MAX_ITERATIONS_DEFAULT = 10
|
|
25
|
+
GOAL_MAX_ITERATIONS_FLOOR = 1
|
|
26
|
+
GOAL_MAX_ITERATIONS_CEILING = 1000
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _get_goal_max_iterations() -> int:
|
|
30
|
+
"""Read the configured /goal iteration cap, with sane fallbacks."""
|
|
31
|
+
val = get_value("goal_max_iterations")
|
|
32
|
+
try:
|
|
33
|
+
n = int(val) if val else GOAL_MAX_ITERATIONS_DEFAULT
|
|
34
|
+
except (ValueError, TypeError):
|
|
35
|
+
n = GOAL_MAX_ITERATIONS_DEFAULT
|
|
36
|
+
return max(GOAL_MAX_ITERATIONS_FLOOR, min(n, GOAL_MAX_ITERATIONS_CEILING))
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# Slash commands
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@register_command(
|
|
43
|
+
name="wiggum",
|
|
44
|
+
description="Loop mode: re-run the same prompt when agent finishes 🍩",
|
|
45
|
+
usage="/wiggum <prompt>",
|
|
46
|
+
category="plugin",
|
|
47
|
+
)
|
|
48
|
+
def handle_wiggum_command(command: str) -> str | bool:
|
|
49
|
+
"""Start Wiggum loop mode and execute the prompt immediately."""
|
|
50
|
+
prompt = _extract_prompt(command)
|
|
51
|
+
if not prompt:
|
|
52
|
+
emit_warning("Usage: /wiggum <prompt>")
|
|
53
|
+
emit_info("Example: /wiggum say hello world")
|
|
54
|
+
emit_info("Press Ctrl+C or run /wiggum_stop to stop the loop.")
|
|
55
|
+
return True
|
|
56
|
+
|
|
57
|
+
state.start(prompt, mode="wiggum")
|
|
58
|
+
emit_success("🍩 WIGGUM MODE ACTIVATED!")
|
|
59
|
+
emit_info(f"Prompt: {prompt}")
|
|
60
|
+
emit_info("The agent will re-loop this prompt after each completion.")
|
|
61
|
+
emit_info("Press Ctrl+C or run /wiggum_stop to stop the loop.")
|
|
62
|
+
return prompt
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@register_command(
|
|
66
|
+
name="goal",
|
|
67
|
+
description="Retry a task until all LLM judges say it is complete 🎯",
|
|
68
|
+
usage="/goal <prompt> | /goal resume <UUID>",
|
|
69
|
+
# /kibble and /chow are puppy-themed aliases for /goal — same behavior,
|
|
70
|
+
# just more on-brand for a code puppy.
|
|
71
|
+
aliases=["kibble", "chow"],
|
|
72
|
+
category="plugin",
|
|
73
|
+
)
|
|
74
|
+
def handle_goal_command(command: str) -> str | bool:
|
|
75
|
+
"""Start goal mode and execute the prompt immediately."""
|
|
76
|
+
prompt = _extract_prompt(command)
|
|
77
|
+
if not prompt:
|
|
78
|
+
emit_warning(
|
|
79
|
+
"Usage: /goal <prompt> | /goal resume <UUID> (aliases: /kibble, /chow)"
|
|
80
|
+
)
|
|
81
|
+
emit_info("Example: /goal make tests pass for the auth flow")
|
|
82
|
+
emit_info("Press Ctrl+C or run /goal_stop to stop the loop.")
|
|
83
|
+
return True
|
|
84
|
+
|
|
85
|
+
if prompt.lower().startswith("resume"):
|
|
86
|
+
return _resume_goal(prompt)
|
|
87
|
+
|
|
88
|
+
run = goal_runs.create(prompt)
|
|
89
|
+
state.start(prompt, mode="goal", run_id=run.run_id)
|
|
90
|
+
_display_banner_message(
|
|
91
|
+
"GOAL MODE", "\U0001f3af ACTIVATED!", banner_name="llm_judge"
|
|
92
|
+
)
|
|
93
|
+
emit_info(f"Goal: {prompt}")
|
|
94
|
+
emit_info(f"Goal UUID: {run.run_id}")
|
|
95
|
+
emit_info(
|
|
96
|
+
"After each iteration, every enabled LLM judge will verify completion in parallel."
|
|
97
|
+
)
|
|
98
|
+
emit_info(
|
|
99
|
+
f"Max iterations: {_get_goal_max_iterations()} "
|
|
100
|
+
f"(change with /set goal_max_iterations=<int>)"
|
|
101
|
+
)
|
|
102
|
+
_emit_configured_judges_summary()
|
|
103
|
+
return prompt
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@register_command(
|
|
107
|
+
name="wiggum_stop",
|
|
108
|
+
description="Stop Wiggum/goal loop mode",
|
|
109
|
+
usage="/wiggum_stop",
|
|
110
|
+
aliases=["stopwiggum", "ws", "goal_stop"],
|
|
111
|
+
category="plugin",
|
|
112
|
+
)
|
|
113
|
+
def handle_wiggum_stop_command(command: str) -> bool:
|
|
114
|
+
"""Stop Wiggum/goal loop mode."""
|
|
115
|
+
del command
|
|
116
|
+
if state.is_active():
|
|
117
|
+
run_id = state.get_state().run_id
|
|
118
|
+
_persist_goal_progress(status="interrupted")
|
|
119
|
+
state.stop()
|
|
120
|
+
emit_success("\U0001f369 Wiggum/goal mode stopped!")
|
|
121
|
+
if run_id:
|
|
122
|
+
emit_info(f"Resume it with: /goal resume {run_id}")
|
|
123
|
+
else:
|
|
124
|
+
emit_info("Wiggum/goal mode is not active.")
|
|
125
|
+
return True
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
@register_command(
|
|
129
|
+
name="judges",
|
|
130
|
+
description="Configure goal-mode LLM judges (TUI) ⚖️",
|
|
131
|
+
usage="/judges",
|
|
132
|
+
category="plugin",
|
|
133
|
+
)
|
|
134
|
+
def handle_judges_command(command: str) -> bool:
|
|
135
|
+
"""Open the goal-judges TUI."""
|
|
136
|
+
del command
|
|
137
|
+
import concurrent.futures
|
|
138
|
+
|
|
139
|
+
from .judges_menu import interactive_judges_menu
|
|
140
|
+
|
|
141
|
+
# The menu is async; run it in a fresh event loop on a worker thread so
|
|
142
|
+
# we don't collide with whatever loop the CLI is using.
|
|
143
|
+
try:
|
|
144
|
+
with concurrent.futures.ThreadPoolExecutor() as executor:
|
|
145
|
+
future = executor.submit(lambda: asyncio.run(interactive_judges_menu()))
|
|
146
|
+
future.result(timeout=600)
|
|
147
|
+
except concurrent.futures.TimeoutError:
|
|
148
|
+
emit_warning("Judges menu timed out.")
|
|
149
|
+
except Exception as exc:
|
|
150
|
+
emit_warning(f"Judges menu error: {exc}")
|
|
151
|
+
|
|
152
|
+
return True
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
# Helpers
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def _emit_configured_judges_summary() -> None:
|
|
159
|
+
"""Show the user exactly which judges /goal will fan out to.
|
|
160
|
+
|
|
161
|
+
Previously we just told users to run /judges, which left people
|
|
162
|
+
wondering if they'd configured anything at all. Now we list the
|
|
163
|
+
enabled judges (and warn about disabled ones) so the state of the
|
|
164
|
+
world is obvious before the loop kicks off.
|
|
165
|
+
"""
|
|
166
|
+
registry = load_judges()
|
|
167
|
+
enabled = registry.enabled()
|
|
168
|
+
disabled = [j for j in registry.judges if not j.enabled]
|
|
169
|
+
|
|
170
|
+
if enabled:
|
|
171
|
+
emit_info(f"Configured judges ({len(enabled)} enabled):")
|
|
172
|
+
for judge in enabled:
|
|
173
|
+
emit_info(f" • {judge.name} ({judge.model})")
|
|
174
|
+
if disabled:
|
|
175
|
+
disabled_names = ", ".join(j.name for j in disabled)
|
|
176
|
+
emit_info(f" (disabled: {disabled_names})")
|
|
177
|
+
else:
|
|
178
|
+
emit_info(
|
|
179
|
+
"No judges configured — falling back to a single default judge "
|
|
180
|
+
"using the implementor's model."
|
|
181
|
+
)
|
|
182
|
+
emit_info("Run /judges to add, edit, enable, or disable judges.")
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def _resume_goal(arguments: str) -> str | bool:
|
|
186
|
+
"""Restore a durable goal run and dispatch its next iteration."""
|
|
187
|
+
parts = arguments.split()
|
|
188
|
+
if len(parts) != 2 or parts[0].lower() != "resume":
|
|
189
|
+
emit_warning("Usage: /goal resume <UUID>")
|
|
190
|
+
return True
|
|
191
|
+
|
|
192
|
+
run = goal_runs.load(parts[1])
|
|
193
|
+
if run is None:
|
|
194
|
+
emit_warning(f"Goal run not found: {parts[1]}")
|
|
195
|
+
return True
|
|
196
|
+
if run.status in {"completed", "stopped"}:
|
|
197
|
+
emit_warning(f"Goal run {run.run_id} is already {run.status}.")
|
|
198
|
+
return True
|
|
199
|
+
|
|
200
|
+
state.start(
|
|
201
|
+
run.prompt,
|
|
202
|
+
mode="goal",
|
|
203
|
+
run_id=run.run_id,
|
|
204
|
+
loop_count=run.loop_count,
|
|
205
|
+
remediation_notes=run.remediation_notes,
|
|
206
|
+
)
|
|
207
|
+
goal_runs.update(
|
|
208
|
+
run.run_id,
|
|
209
|
+
loop_count=run.loop_count,
|
|
210
|
+
remediation_notes=run.remediation_notes,
|
|
211
|
+
)
|
|
212
|
+
_display_banner_message("GOAL MODE", "\U0001f3af RESUMED!", banner_name="llm_judge")
|
|
213
|
+
emit_info(f"Goal UUID: {run.run_id}")
|
|
214
|
+
emit_info(f"Resuming after {run.loop_count} completed iteration(s).")
|
|
215
|
+
if run.remediation_notes:
|
|
216
|
+
return f"{run.prompt}\n\nJudge remediation notes:\n{run.remediation_notes}"
|
|
217
|
+
return run.prompt
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def _extract_prompt(command: str) -> str:
|
|
221
|
+
parts = command.split(maxsplit=1)
|
|
222
|
+
if len(parts) < 2:
|
|
223
|
+
return ""
|
|
224
|
+
return parts[1].strip()
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def _response_text(result: Any) -> str | None:
|
|
228
|
+
return str(getattr(result, "output", "")) if result is not None else None
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def _display_banner_message(
|
|
232
|
+
label: str,
|
|
233
|
+
message: str,
|
|
234
|
+
*,
|
|
235
|
+
banner_name: str,
|
|
236
|
+
details: str | None = None,
|
|
237
|
+
final: bool = False,
|
|
238
|
+
) -> None:
|
|
239
|
+
"""Display an inline banner followed by a message.
|
|
240
|
+
|
|
241
|
+
Plain sequential output — it scrolls inside the bottom bar's region,
|
|
242
|
+
so no terminal-ownership dance is needed.
|
|
243
|
+
"""
|
|
244
|
+
from rich.console import Console
|
|
245
|
+
from rich.text import Text
|
|
246
|
+
|
|
247
|
+
from code_puppy.config import get_banner_color
|
|
248
|
+
from code_puppy.tools.display import erase_progress_line
|
|
249
|
+
|
|
250
|
+
console = Console()
|
|
251
|
+
|
|
252
|
+
erase_progress_line(console)
|
|
253
|
+
console.print()
|
|
254
|
+
color = get_banner_color(banner_name)
|
|
255
|
+
banner = Text.from_markup(
|
|
256
|
+
f"[bold white on {color}] {label} [/bold white on {color}] "
|
|
257
|
+
)
|
|
258
|
+
console.print(banner, end="")
|
|
259
|
+
# markup=False so brackets in the message (e.g. "[joe-brown]") aren't
|
|
260
|
+
# eaten by Rich's markup parser. Same for `details`.
|
|
261
|
+
console.print(message, markup=False, highlight=False)
|
|
262
|
+
|
|
263
|
+
if details:
|
|
264
|
+
console.print(details, markup=False, highlight=False)
|
|
265
|
+
if final:
|
|
266
|
+
console.print()
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def _display_llm_judge(
|
|
270
|
+
message: str, details: str | None = None, *, final: bool = False
|
|
271
|
+
) -> None:
|
|
272
|
+
"""Display goal-judge output with an inline banner."""
|
|
273
|
+
_display_banner_message(
|
|
274
|
+
"LLM JUDGE",
|
|
275
|
+
message,
|
|
276
|
+
banner_name="llm_judge",
|
|
277
|
+
details=details,
|
|
278
|
+
final=final,
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def _resolve_judges(implementor_agent: Any) -> list[JudgeConfig]:
|
|
283
|
+
"""Pick the judge set for this /goal iteration.
|
|
284
|
+
|
|
285
|
+
If the user has configured judges via ``/judges`` we use those.
|
|
286
|
+
Otherwise we fall back to a single ``default`` judge that uses the
|
|
287
|
+
implementor agent's model and the standard goal-judge prompt.
|
|
288
|
+
"""
|
|
289
|
+
fallback_model = getattr(
|
|
290
|
+
implementor_agent.get_pydantic_agent().model
|
|
291
|
+
if hasattr(implementor_agent, "get_pydantic_agent")
|
|
292
|
+
else None,
|
|
293
|
+
"model_name",
|
|
294
|
+
None,
|
|
295
|
+
)
|
|
296
|
+
if not fallback_model:
|
|
297
|
+
# The agent object exposes its model name via get_model_name() in most
|
|
298
|
+
# places; fall back to that if the pydantic_agent shape differs.
|
|
299
|
+
try:
|
|
300
|
+
fallback_model = implementor_agent.get_model_name()
|
|
301
|
+
except Exception:
|
|
302
|
+
fallback_model = "code-puppy"
|
|
303
|
+
|
|
304
|
+
return get_enabled_judges_or_default(str(fallback_model))
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def _format_remediation_block(verdicts: list[GoalJudgement]) -> str:
|
|
308
|
+
"""Build the remediation-notes string that feeds the next iteration."""
|
|
309
|
+
lines: list[str] = []
|
|
310
|
+
for v in verdicts:
|
|
311
|
+
if v.abstained:
|
|
312
|
+
status = "⚠️ ABSTAIN"
|
|
313
|
+
else:
|
|
314
|
+
status = "✅ PASS" if v.complete else "❌ FAIL"
|
|
315
|
+
lines.append(f"[{v.judge_name}] {status}")
|
|
316
|
+
if v.notes:
|
|
317
|
+
for note_line in v.notes.strip().splitlines():
|
|
318
|
+
lines.append(f" {note_line}")
|
|
319
|
+
lines.append("")
|
|
320
|
+
return "\n".join(lines).rstrip()
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
# Judge orchestration (parallel)
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
async def _run_single_judge(
|
|
327
|
+
judge_config: JudgeConfig,
|
|
328
|
+
*,
|
|
329
|
+
implementor_agent: Any,
|
|
330
|
+
goal: str,
|
|
331
|
+
response: str | None,
|
|
332
|
+
error: BaseException | None,
|
|
333
|
+
history: list[Any],
|
|
334
|
+
) -> GoalJudgement:
|
|
335
|
+
"""Run a single judge. No I/O — callers handle display before/after.
|
|
336
|
+
|
|
337
|
+
We intentionally do NOT print here: ``_run_goal_judges`` runs many of
|
|
338
|
+
these in parallel via ``asyncio.gather``, and concurrent calls into the
|
|
339
|
+
rich Console (which does \\r line-clearing tricks) interleave and
|
|
340
|
+
overwrite each other. Display is serialized at the orchestrator level.
|
|
341
|
+
"""
|
|
342
|
+
try:
|
|
343
|
+
return await judge_goal(
|
|
344
|
+
judge_config=judge_config,
|
|
345
|
+
implementor_agent=implementor_agent,
|
|
346
|
+
goal=goal,
|
|
347
|
+
response=response,
|
|
348
|
+
error=error,
|
|
349
|
+
history=history,
|
|
350
|
+
)
|
|
351
|
+
except (asyncio.CancelledError, KeyboardInterrupt):
|
|
352
|
+
raise
|
|
353
|
+
except Exception as exc:
|
|
354
|
+
# judge_goal handles model errors; unexpected plumbing errors still abstain
|
|
355
|
+
# so one broken judge cannot block /goal.
|
|
356
|
+
from code_puppy.error_logging import log_error
|
|
357
|
+
|
|
358
|
+
log_error(exc, context=f"Goal judge failed ({judge_config.name})")
|
|
359
|
+
return GoalJudgement(
|
|
360
|
+
judge_name=judge_config.name,
|
|
361
|
+
complete=False,
|
|
362
|
+
notes=f"judge crashed: {type(exc).__name__}: {exc}",
|
|
363
|
+
raw_response="",
|
|
364
|
+
abstained=True,
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
def _judge_roster_line(judges: list[JudgeConfig]) -> str:
|
|
369
|
+
"""e.g. 'judy (gpt-5.4), joe-brown (claude-sonnet-4.5)'."""
|
|
370
|
+
return ", ".join(f"{j.name} ({j.model})" for j in judges)
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
async def _run_goal_judges(
|
|
374
|
+
*,
|
|
375
|
+
agent: Any,
|
|
376
|
+
goal: str,
|
|
377
|
+
result: Any,
|
|
378
|
+
error: BaseException | None,
|
|
379
|
+
) -> tuple[bool, str, list[GoalJudgement]]:
|
|
380
|
+
"""Run every enabled judge in parallel.
|
|
381
|
+
|
|
382
|
+
Returns ``(all_complete, formatted_notes, verdicts)``.
|
|
383
|
+
|
|
384
|
+
``all_complete`` is True when every judge reports ``complete=True``.
|
|
385
|
+
The judge's own ``complete=True`` IS the "no remediation needed"
|
|
386
|
+
signal — any rationale notes alongside it are just for visibility and
|
|
387
|
+
don't block completion.
|
|
388
|
+
"""
|
|
389
|
+
judges = _resolve_judges(agent)
|
|
390
|
+
if not judges:
|
|
391
|
+
return False, "No judge agents configured.", []
|
|
392
|
+
|
|
393
|
+
history = list(agent.get_message_history())
|
|
394
|
+
response_text = _response_text(result)
|
|
395
|
+
|
|
396
|
+
# Announce up front so the user knows we're firing N judges in parallel.
|
|
397
|
+
if len(judges) == 1:
|
|
398
|
+
_display_llm_judge(
|
|
399
|
+
f"Asking judge {_judge_roster_line(judges)} if the goal is complete..."
|
|
400
|
+
)
|
|
401
|
+
else:
|
|
402
|
+
_display_llm_judge(
|
|
403
|
+
f"Running {len(judges)} judges in parallel: {_judge_roster_line(judges)}"
|
|
404
|
+
)
|
|
405
|
+
|
|
406
|
+
try:
|
|
407
|
+
verdicts: list[GoalJudgement] = await asyncio.gather(
|
|
408
|
+
*(
|
|
409
|
+
_run_single_judge(
|
|
410
|
+
judge,
|
|
411
|
+
implementor_agent=agent,
|
|
412
|
+
goal=goal,
|
|
413
|
+
response=response_text,
|
|
414
|
+
error=error,
|
|
415
|
+
history=history,
|
|
416
|
+
)
|
|
417
|
+
for judge in judges
|
|
418
|
+
)
|
|
419
|
+
)
|
|
420
|
+
except (asyncio.CancelledError, KeyboardInterrupt):
|
|
421
|
+
# Show why judges bailed, then re-raise so the caller keeps the REPL alive
|
|
422
|
+
# and stops the loop; a sentinel would trigger another retry.
|
|
423
|
+
_display_llm_judge("⛔ Judges cancelled (Ctrl+C). Stopping goal loop.")
|
|
424
|
+
raise
|
|
425
|
+
|
|
426
|
+
# Now serialize the per-judge verdicts so all banners actually show up.
|
|
427
|
+
for v in verdicts:
|
|
428
|
+
if v.abstained:
|
|
429
|
+
glyph = "⚠️ ABSTAIN"
|
|
430
|
+
else:
|
|
431
|
+
glyph = "✅ PASS" if v.complete else "❌ FAIL"
|
|
432
|
+
summary = v.notes.strip().splitlines()[0] if v.notes.strip() else "(no notes)"
|
|
433
|
+
_display_llm_judge(f" [{v.judge_name}] {glyph} — {summary}")
|
|
434
|
+
|
|
435
|
+
# Abstentions do not vote; completion requires every non-abstaining judge to
|
|
436
|
+
# pass. If all abstain, report incomplete because completion is undecidable.
|
|
437
|
+
voting = [v for v in verdicts if not v.abstained]
|
|
438
|
+
if not voting:
|
|
439
|
+
all_complete = False
|
|
440
|
+
if verdicts:
|
|
441
|
+
_display_llm_judge("⚠️ All judges abstained — cannot determine completion.")
|
|
442
|
+
else:
|
|
443
|
+
all_complete = all(v.complete for v in voting)
|
|
444
|
+
|
|
445
|
+
notes = _format_remediation_block(verdicts)
|
|
446
|
+
return all_complete, notes, verdicts
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
# Turn-end hook (drives /goal and /wiggum loops)
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
async def _on_interactive_turn_end(
|
|
453
|
+
agent: Any,
|
|
454
|
+
prompt: str,
|
|
455
|
+
result: Any = None,
|
|
456
|
+
*,
|
|
457
|
+
success: bool = True,
|
|
458
|
+
error: BaseException | None = None,
|
|
459
|
+
) -> dict[str, Any] | None:
|
|
460
|
+
"""Ask the CLI to continue while Wiggum/goal mode is active."""
|
|
461
|
+
del prompt, success
|
|
462
|
+
goal_prompt = state.get_prompt()
|
|
463
|
+
if not goal_prompt:
|
|
464
|
+
state.stop()
|
|
465
|
+
return None
|
|
466
|
+
|
|
467
|
+
loop_num = state.increment()
|
|
468
|
+
if state.is_goal_mode():
|
|
469
|
+
try:
|
|
470
|
+
complete, notes, _verdicts = await _run_goal_judges(
|
|
471
|
+
agent=agent,
|
|
472
|
+
goal=goal_prompt,
|
|
473
|
+
result=result,
|
|
474
|
+
error=error,
|
|
475
|
+
)
|
|
476
|
+
except (asyncio.CancelledError, KeyboardInterrupt):
|
|
477
|
+
# Keep enough durable state for an explicit UUID resume.
|
|
478
|
+
run_id = state.get_state().run_id
|
|
479
|
+
_persist_goal_progress(status="interrupted")
|
|
480
|
+
_display_llm_judge("\u26d4 Goal loop cancelled (Ctrl+C).")
|
|
481
|
+
state.stop()
|
|
482
|
+
if run_id:
|
|
483
|
+
emit_info(f"Resume it with: /goal resume {run_id}")
|
|
484
|
+
return None
|
|
485
|
+
if complete:
|
|
486
|
+
_persist_goal_progress(status="completed")
|
|
487
|
+
# Per-judge verdicts were already shown by _run_goal_judges —
|
|
488
|
+
# no need to re-dump the notes block here.
|
|
489
|
+
_display_llm_judge("✅ GOAL COMPLETE!", final=True)
|
|
490
|
+
state.stop()
|
|
491
|
+
return None
|
|
492
|
+
|
|
493
|
+
max_iters = _get_goal_max_iterations()
|
|
494
|
+
if loop_num >= max_iters:
|
|
495
|
+
_persist_goal_progress(status="stopped")
|
|
496
|
+
_display_llm_judge(
|
|
497
|
+
f"🛑 GOAL STOPPED — Hit max iterations ({max_iters}). "
|
|
498
|
+
f"Raise the cap with /set goal_max_iterations=<int>.",
|
|
499
|
+
final=True,
|
|
500
|
+
)
|
|
501
|
+
state.stop()
|
|
502
|
+
return None
|
|
503
|
+
|
|
504
|
+
state.get_state().remediation_notes = notes
|
|
505
|
+
_persist_goal_progress()
|
|
506
|
+
_display_llm_judge(
|
|
507
|
+
f"❌ GOAL INCOMPLETE — Retrying! (Loop #{loop_num}/{max_iters})",
|
|
508
|
+
final=True,
|
|
509
|
+
)
|
|
510
|
+
return {
|
|
511
|
+
"prompt": f"{goal_prompt}\n\nJudge remediation notes:\n{notes}",
|
|
512
|
+
"clear_context": True,
|
|
513
|
+
"delay": 0.5,
|
|
514
|
+
"reason": "goal",
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
if error is not None:
|
|
518
|
+
emit_warning(f"\n🍩 WIGGUM RETRYING AFTER ERROR! (Loop #{loop_num})")
|
|
519
|
+
emit_system_message(f"Previous run failed: {error}")
|
|
520
|
+
else:
|
|
521
|
+
emit_warning(f"\n🍩 WIGGUM RELOOPING! (Loop #{loop_num})")
|
|
522
|
+
|
|
523
|
+
emit_system_message(f"Re-running prompt: {goal_prompt}")
|
|
524
|
+
return {
|
|
525
|
+
"prompt": goal_prompt,
|
|
526
|
+
"clear_context": True,
|
|
527
|
+
"delay": 0.5,
|
|
528
|
+
"reason": "wiggum",
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
|
|
532
|
+
def _persist_goal_progress(*, status: str = "active") -> None:
|
|
533
|
+
current = state.get_state()
|
|
534
|
+
if current.mode != "goal" or current.run_id is None:
|
|
535
|
+
return
|
|
536
|
+
goal_runs.update(
|
|
537
|
+
current.run_id,
|
|
538
|
+
loop_count=current.loop_count,
|
|
539
|
+
remediation_notes=current.remediation_notes,
|
|
540
|
+
status=status,
|
|
541
|
+
)
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
def _on_interactive_turn_cancel(prompt: str, *, reason: str = "cancelled") -> None:
|
|
545
|
+
del prompt
|
|
546
|
+
if state.is_active():
|
|
547
|
+
current = state.get_state()
|
|
548
|
+
run_id = current.run_id
|
|
549
|
+
_persist_goal_progress(status="interrupted")
|
|
550
|
+
state.stop()
|
|
551
|
+
emit_warning(f"\U0001f369 Wiggum/goal loop stopped due to {reason}")
|
|
552
|
+
if run_id:
|
|
553
|
+
emit_info(f"Resume it with: /goal resume {run_id}")
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
def _autonomous_loop_capability(name: str) -> bool | None:
|
|
557
|
+
"""Advertise the autonomous-loop state through ``feature_capability``.
|
|
558
|
+
|
|
559
|
+
Core (e.g. ask_user_question) checks ``autonomous_loop`` to decide
|
|
560
|
+
whether interactive tools should be blocked, without importing wiggum.
|
|
561
|
+
"""
|
|
562
|
+
if name != "autonomous_loop":
|
|
563
|
+
return None
|
|
564
|
+
return state.is_active()
|
|
565
|
+
|
|
566
|
+
|
|
567
|
+
register_callback("interactive_turn_end", _on_interactive_turn_end)
|
|
568
|
+
register_callback("interactive_turn_cancel", _on_interactive_turn_cancel)
|
|
569
|
+
register_callback("feature_capability", _autonomous_loop_capability)
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""State for the Wiggum/goal continuation plugin."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class WiggumState:
|
|
10
|
+
"""Tiny state container. No behavior soup, please and thank you."""
|
|
11
|
+
|
|
12
|
+
active: bool = False
|
|
13
|
+
prompt: str | None = None
|
|
14
|
+
loop_count: int = 0
|
|
15
|
+
mode: str = "wiggum"
|
|
16
|
+
remediation_notes: str | None = None
|
|
17
|
+
run_id: str | None = None
|
|
18
|
+
|
|
19
|
+
def start(
|
|
20
|
+
self,
|
|
21
|
+
prompt: str,
|
|
22
|
+
*,
|
|
23
|
+
mode: str = "wiggum",
|
|
24
|
+
run_id: str | None = None,
|
|
25
|
+
loop_count: int = 0,
|
|
26
|
+
remediation_notes: str | None = None,
|
|
27
|
+
) -> None:
|
|
28
|
+
self.active = True
|
|
29
|
+
self.prompt = prompt
|
|
30
|
+
self.loop_count = loop_count
|
|
31
|
+
self.mode = mode
|
|
32
|
+
self.remediation_notes = remediation_notes
|
|
33
|
+
self.run_id = run_id
|
|
34
|
+
|
|
35
|
+
def stop(self) -> None:
|
|
36
|
+
self.active = False
|
|
37
|
+
self.prompt = None
|
|
38
|
+
self.loop_count = 0
|
|
39
|
+
self.mode = "wiggum"
|
|
40
|
+
self.remediation_notes = None
|
|
41
|
+
self.run_id = None
|
|
42
|
+
|
|
43
|
+
def increment(self) -> int:
|
|
44
|
+
self.loop_count += 1
|
|
45
|
+
return self.loop_count
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
_STATE = WiggumState()
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def get_state() -> WiggumState:
|
|
52
|
+
return _STATE
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def is_active() -> bool:
|
|
56
|
+
return _STATE.active
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def is_goal_mode() -> bool:
|
|
60
|
+
return _STATE.active and _STATE.mode == "goal"
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def get_prompt() -> str | None:
|
|
64
|
+
return _STATE.prompt if _STATE.active else None
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def start(
|
|
68
|
+
prompt: str,
|
|
69
|
+
*,
|
|
70
|
+
mode: str = "wiggum",
|
|
71
|
+
run_id: str | None = None,
|
|
72
|
+
loop_count: int = 0,
|
|
73
|
+
remediation_notes: str | None = None,
|
|
74
|
+
) -> None:
|
|
75
|
+
_STATE.start(
|
|
76
|
+
prompt,
|
|
77
|
+
mode=mode,
|
|
78
|
+
run_id=run_id,
|
|
79
|
+
loop_count=loop_count,
|
|
80
|
+
remediation_notes=remediation_notes,
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def stop() -> None:
|
|
85
|
+
_STATE.stop()
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def increment() -> int:
|
|
89
|
+
return _STATE.increment()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Runtime-only CLI and session controls for YOLO mode."""
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Expose a runtime-only YOLO override through the CLI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
from typing import Any, Optional
|
|
7
|
+
|
|
8
|
+
from code_puppy.callbacks import register_callback
|
|
9
|
+
from code_puppy.config import set_cli_yolo_override
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _parse_bool(value: str) -> bool:
|
|
13
|
+
"""Parse the explicit boolean syntax used by ``--yolo``."""
|
|
14
|
+
normalized = value.strip().lower()
|
|
15
|
+
if normalized == "true":
|
|
16
|
+
return True
|
|
17
|
+
if normalized == "false":
|
|
18
|
+
return False
|
|
19
|
+
raise argparse.ArgumentTypeError("expected 'true' or 'false'")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _register_cli_args(parser: Any) -> None:
|
|
23
|
+
parser.add_argument(
|
|
24
|
+
"--yolo",
|
|
25
|
+
type=_parse_bool,
|
|
26
|
+
default=None,
|
|
27
|
+
metavar="{true,false}",
|
|
28
|
+
help=(
|
|
29
|
+
"Override YOLO mode for this run without changing puppy.cfg "
|
|
30
|
+
"(/set yolo_mode takes precedence)"
|
|
31
|
+
),
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _handle_cli_args(args: Any) -> Optional[dict]:
|
|
36
|
+
set_cli_yolo_override(getattr(args, "yolo", None))
|
|
37
|
+
return None
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
register_callback("register_cli_args", _register_cli_args)
|
|
41
|
+
register_callback("handle_cli_args", _handle_cli_args)
|