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,449 @@
|
|
|
1
|
+
"""Agent-facing tools for the puppy_kennel.
|
|
2
|
+
|
|
3
|
+
Full tool surface:
|
|
4
|
+
|
|
5
|
+
* ``kennel_recall`` — BM25 search across wings
|
|
6
|
+
* ``kennel_remember`` — explicit verbatim write to a chosen wing
|
|
7
|
+
* ``kennel_recent`` — recent drawers without a query (timeline view)
|
|
8
|
+
* ``kennel_list_wings`` — discover wings + drawer counts
|
|
9
|
+
* ``kennel_stats`` — kennel-wide totals + on-disk size
|
|
10
|
+
|
|
11
|
+
All tools share a single ``register_tools_callback`` plugin entrypoint.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
from pydantic import BaseModel, Field
|
|
19
|
+
from pydantic_ai import RunContext
|
|
20
|
+
|
|
21
|
+
from . import kennel
|
|
22
|
+
from .config import DB_PATH
|
|
23
|
+
from .state import DISABLED_TOOL_ERROR, is_enabled
|
|
24
|
+
from .wings import (
|
|
25
|
+
USER_WING,
|
|
26
|
+
agent_wing,
|
|
27
|
+
default_recall_scope,
|
|
28
|
+
detect_cwd,
|
|
29
|
+
repo_wing,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class KennelDrawer(BaseModel):
|
|
34
|
+
"""A single recalled drawer in tool output form."""
|
|
35
|
+
|
|
36
|
+
id: int
|
|
37
|
+
room_id: int
|
|
38
|
+
role: str | None = None
|
|
39
|
+
content: str
|
|
40
|
+
ts: str
|
|
41
|
+
session_id: str | None = None
|
|
42
|
+
agent: str | None = None
|
|
43
|
+
cwd: str | None = None
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class KennelRecallOutput(BaseModel):
|
|
47
|
+
"""Output for ``kennel_recall``."""
|
|
48
|
+
|
|
49
|
+
query: str
|
|
50
|
+
wings_searched: list[str]
|
|
51
|
+
total_hits: int
|
|
52
|
+
drawers: list[KennelDrawer] = Field(default_factory=list)
|
|
53
|
+
error: str | None = None
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class KennelRememberOutput(BaseModel):
|
|
57
|
+
"""Output for ``kennel_remember``."""
|
|
58
|
+
|
|
59
|
+
drawer_id: int
|
|
60
|
+
wing: str
|
|
61
|
+
room: str
|
|
62
|
+
bytes_stored: int
|
|
63
|
+
error: str | None = None
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class KennelRecentOutput(BaseModel):
|
|
67
|
+
"""Output for ``kennel_recent``."""
|
|
68
|
+
|
|
69
|
+
wings_searched: list[str]
|
|
70
|
+
total: int
|
|
71
|
+
drawers: list[KennelDrawer] = Field(default_factory=list)
|
|
72
|
+
error: str | None = None
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class WingInfo(BaseModel):
|
|
76
|
+
name: str
|
|
77
|
+
drawer_count: int
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class KennelWingsOutput(BaseModel):
|
|
81
|
+
"""Output for ``kennel_list_wings``."""
|
|
82
|
+
|
|
83
|
+
wings: list[WingInfo] = Field(default_factory=list)
|
|
84
|
+
total_wings: int
|
|
85
|
+
error: str | None = None
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class KennelStatsOutput(BaseModel):
|
|
89
|
+
"""Output for ``kennel_stats``."""
|
|
90
|
+
|
|
91
|
+
db_path: str
|
|
92
|
+
db_size_bytes: int
|
|
93
|
+
total_drawers: int
|
|
94
|
+
total_wings: int
|
|
95
|
+
error: str | None = None
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _drawer_to_model(d: Any) -> KennelDrawer:
|
|
99
|
+
meta = d.metadata or {}
|
|
100
|
+
return KennelDrawer(
|
|
101
|
+
id=d.id,
|
|
102
|
+
room_id=d.room_id,
|
|
103
|
+
role=d.role,
|
|
104
|
+
content=d.content,
|
|
105
|
+
ts=d.ts,
|
|
106
|
+
session_id=d.session_id,
|
|
107
|
+
agent=meta.get("agent"),
|
|
108
|
+
cwd=meta.get("cwd"),
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _resolve_wing(value: str, agent_name: str, cwd: Any) -> str:
|
|
113
|
+
"""Turn a wing shortcut into a concrete wing name.
|
|
114
|
+
|
|
115
|
+
Shortcuts:
|
|
116
|
+
``"repo"`` -> current repo wing (the default for writes)
|
|
117
|
+
``"agent"`` -> this agent's diary wing
|
|
118
|
+
``"user"`` -> the global user-prefs wing
|
|
119
|
+
``""`` -> defaults to ``repo`` (most writes are project-scoped)
|
|
120
|
+
anything else is treated as an explicit wing name.
|
|
121
|
+
"""
|
|
122
|
+
v = (value or "").strip()
|
|
123
|
+
if v == "" or v == "repo":
|
|
124
|
+
return repo_wing(cwd)
|
|
125
|
+
if v == "agent":
|
|
126
|
+
return agent_wing(agent_name)
|
|
127
|
+
if v == "user":
|
|
128
|
+
return USER_WING
|
|
129
|
+
return v
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _resolve_scope(wing: str, scope: str, agent_name: str, cwd: Any) -> list[str]:
|
|
133
|
+
"""Turn (wing, scope) into the list of wings to search.
|
|
134
|
+
|
|
135
|
+
Returns an empty list to mean "no wing filter — search everything."
|
|
136
|
+
"""
|
|
137
|
+
w = (wing or "").strip()
|
|
138
|
+
if w:
|
|
139
|
+
return [_resolve_wing(w, agent_name, cwd)]
|
|
140
|
+
if scope == "repo":
|
|
141
|
+
return [repo_wing(cwd)]
|
|
142
|
+
if scope == "agent":
|
|
143
|
+
return [agent_wing(agent_name)]
|
|
144
|
+
if scope == "user":
|
|
145
|
+
return [USER_WING]
|
|
146
|
+
if scope == "all":
|
|
147
|
+
return [] # caller treats empty as "no filter"
|
|
148
|
+
# "default" or anything weird
|
|
149
|
+
return default_recall_scope(agent_name, cwd)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def register_kennel_recall(agent: Any) -> None:
|
|
153
|
+
"""Register the ``kennel_recall`` tool on the given agent."""
|
|
154
|
+
|
|
155
|
+
@agent.tool
|
|
156
|
+
async def kennel_recall(
|
|
157
|
+
context: RunContext,
|
|
158
|
+
query: str,
|
|
159
|
+
wing: str = "",
|
|
160
|
+
top_k: int = 5,
|
|
161
|
+
scope: str = "default",
|
|
162
|
+
) -> KennelRecallOutput:
|
|
163
|
+
"""Search the Puppy Kennel for relevant past drawers (verbatim memories).
|
|
164
|
+
|
|
165
|
+
The kennel stores prior agent responses scoped by wing:
|
|
166
|
+
* ``repo:<path>`` — project memory shared across agents in that repo
|
|
167
|
+
* ``agent:<name>`` — this agent's private diary
|
|
168
|
+
* ``user:default`` — cross-cutting user preferences
|
|
169
|
+
|
|
170
|
+
Args:
|
|
171
|
+
query: Free-form text. FTS5 BM25 ranks results.
|
|
172
|
+
wing: Restrict to a single wing (e.g. ``"repo:/Users/me/foo"``).
|
|
173
|
+
Leave blank to use ``scope``.
|
|
174
|
+
top_k: Number of drawers to return (default 5, max 20).
|
|
175
|
+
scope: When ``wing`` is empty, choose the wing set:
|
|
176
|
+
* ``"default"`` — repo + this agent + user (sensible default)
|
|
177
|
+
* ``"repo"`` — only the current repo wing
|
|
178
|
+
* ``"agent"`` — only this agent's diary
|
|
179
|
+
* ``"all"`` — every wing (use sparingly)
|
|
180
|
+
|
|
181
|
+
Returns drawers ordered by BM25 relevance, deduplicated by content.
|
|
182
|
+
"""
|
|
183
|
+
if not is_enabled():
|
|
184
|
+
return KennelRecallOutput(
|
|
185
|
+
query=query or "",
|
|
186
|
+
wings_searched=[],
|
|
187
|
+
total_hits=0,
|
|
188
|
+
error=DISABLED_TOOL_ERROR,
|
|
189
|
+
)
|
|
190
|
+
try:
|
|
191
|
+
top_k = max(1, min(int(top_k), 20))
|
|
192
|
+
except (TypeError, ValueError):
|
|
193
|
+
top_k = 5
|
|
194
|
+
|
|
195
|
+
if not query or not query.strip():
|
|
196
|
+
return KennelRecallOutput(
|
|
197
|
+
query=query or "",
|
|
198
|
+
wings_searched=[],
|
|
199
|
+
total_hits=0,
|
|
200
|
+
error="Empty query — provide some text to search for.",
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
try:
|
|
204
|
+
agent_name = _agent_name_from_context(context)
|
|
205
|
+
cwd = detect_cwd()
|
|
206
|
+
wings_to_search = _resolve_scope(wing, scope, agent_name, cwd)
|
|
207
|
+
|
|
208
|
+
hits = kennel.search_drawers_multi(
|
|
209
|
+
query=query,
|
|
210
|
+
wing_names=wings_to_search or None,
|
|
211
|
+
limit=top_k,
|
|
212
|
+
)
|
|
213
|
+
return KennelRecallOutput(
|
|
214
|
+
query=query,
|
|
215
|
+
wings_searched=wings_to_search,
|
|
216
|
+
total_hits=len(hits),
|
|
217
|
+
drawers=[_drawer_to_model(d) for d in hits],
|
|
218
|
+
)
|
|
219
|
+
except Exception as exc: # noqa: BLE001 — tool must never raise.
|
|
220
|
+
return KennelRecallOutput(
|
|
221
|
+
query=query,
|
|
222
|
+
wings_searched=[],
|
|
223
|
+
total_hits=0,
|
|
224
|
+
error=f"kennel_recall failed: {exc!r}",
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def register_kennel_remember(agent: Any) -> None:
|
|
229
|
+
"""Register the ``kennel_remember`` tool — explicit verbatim write."""
|
|
230
|
+
|
|
231
|
+
@agent.tool
|
|
232
|
+
async def kennel_remember(
|
|
233
|
+
context: RunContext,
|
|
234
|
+
content: str,
|
|
235
|
+
wing: str = "repo",
|
|
236
|
+
room: str = "notes",
|
|
237
|
+
) -> KennelRememberOutput:
|
|
238
|
+
"""Save a verbatim note to the Puppy Kennel.
|
|
239
|
+
|
|
240
|
+
Use this when the user says "remember that..." or when you learn
|
|
241
|
+
something durable that future sessions should know about. Writes
|
|
242
|
+
are best-effort; failures return an error string rather than raising.
|
|
243
|
+
|
|
244
|
+
**Pick the wing deliberately — it determines who sees this memory:**
|
|
245
|
+
|
|
246
|
+
* ``"repo"`` (default) — facts about THIS project: architectural
|
|
247
|
+
decisions, gotchas, conventions, why-we-chose-X. Anyone working
|
|
248
|
+
in this repo will see it. Use this for most "remember that..."
|
|
249
|
+
requests during project work.
|
|
250
|
+
* ``"user"`` — facts about the user (Mike): preferences, style,
|
|
251
|
+
biographical detail. Pervasive across every repo and every
|
|
252
|
+
agent. Use sparingly — only for cross-cutting truths.
|
|
253
|
+
* ``"agent"`` — cross-project learnings about your own behavior
|
|
254
|
+
or limitations. Rare. Most "agent" notes are actually project
|
|
255
|
+
notes wearing a costume — prefer ``"repo"`` unless the lesson
|
|
256
|
+
genuinely transcends projects.
|
|
257
|
+
|
|
258
|
+
Args:
|
|
259
|
+
content: The verbatim text to remember. Required.
|
|
260
|
+
wing: ``"repo"`` (default) / ``"user"`` / ``"agent"`` /
|
|
261
|
+
or an explicit wing name like ``"team:platform"``.
|
|
262
|
+
room: Room name within the wing. Defaults to ``"notes"``.
|
|
263
|
+
Use ``"preferences"`` for user-wing writes,
|
|
264
|
+
``"decisions"`` for project-wide architectural calls.
|
|
265
|
+
"""
|
|
266
|
+
if not is_enabled():
|
|
267
|
+
return KennelRememberOutput(
|
|
268
|
+
drawer_id=0,
|
|
269
|
+
wing="",
|
|
270
|
+
room=room,
|
|
271
|
+
bytes_stored=0,
|
|
272
|
+
error=DISABLED_TOOL_ERROR,
|
|
273
|
+
)
|
|
274
|
+
if not content or not content.strip():
|
|
275
|
+
return KennelRememberOutput(
|
|
276
|
+
drawer_id=0,
|
|
277
|
+
wing="",
|
|
278
|
+
room=room,
|
|
279
|
+
bytes_stored=0,
|
|
280
|
+
error="Empty content — nothing to remember.",
|
|
281
|
+
)
|
|
282
|
+
try:
|
|
283
|
+
agent_name = _agent_name_from_context(context)
|
|
284
|
+
cwd = detect_cwd()
|
|
285
|
+
resolved_wing = _resolve_wing(wing, agent_name, cwd)
|
|
286
|
+
drawer_id = kennel.write_note(
|
|
287
|
+
wing_name=resolved_wing,
|
|
288
|
+
room_name=(room or "notes").strip() or "notes",
|
|
289
|
+
content=content,
|
|
290
|
+
role="note",
|
|
291
|
+
metadata={"agent": agent_name, "cwd": str(cwd), "explicit": True},
|
|
292
|
+
)
|
|
293
|
+
return KennelRememberOutput(
|
|
294
|
+
drawer_id=drawer_id,
|
|
295
|
+
wing=resolved_wing,
|
|
296
|
+
room=room or "notes",
|
|
297
|
+
bytes_stored=len(content.encode("utf-8")),
|
|
298
|
+
)
|
|
299
|
+
except Exception as exc: # noqa: BLE001
|
|
300
|
+
return KennelRememberOutput(
|
|
301
|
+
drawer_id=0,
|
|
302
|
+
wing=wing,
|
|
303
|
+
room=room,
|
|
304
|
+
bytes_stored=0,
|
|
305
|
+
error=f"kennel_remember failed: {exc!r}",
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def register_kennel_recent(agent: Any) -> None:
|
|
310
|
+
"""Register the ``kennel_recent`` tool — time-ordered drawer browsing."""
|
|
311
|
+
|
|
312
|
+
@agent.tool
|
|
313
|
+
async def kennel_recent(
|
|
314
|
+
context: RunContext,
|
|
315
|
+
wing: str = "",
|
|
316
|
+
top_k: int = 5,
|
|
317
|
+
scope: str = "default",
|
|
318
|
+
) -> KennelRecentOutput:
|
|
319
|
+
"""List recent drawers without a query. Useful for orienting on a new session.
|
|
320
|
+
|
|
321
|
+
Args:
|
|
322
|
+
wing: Restrict to a single wing (or shortcut: "repo", "agent", "user").
|
|
323
|
+
Leave blank to use ``scope``.
|
|
324
|
+
top_k: Number of drawers (1-50, default 5).
|
|
325
|
+
scope: When ``wing`` is blank: ``"default"`` / ``"repo"`` / ``"agent"``
|
|
326
|
+
/ ``"user"`` / ``"all"``. Same semantics as ``kennel_recall``.
|
|
327
|
+
|
|
328
|
+
Returns drawers newest-first, deduplicated by content.
|
|
329
|
+
"""
|
|
330
|
+
if not is_enabled():
|
|
331
|
+
return KennelRecentOutput(
|
|
332
|
+
wings_searched=[],
|
|
333
|
+
total=0,
|
|
334
|
+
error=DISABLED_TOOL_ERROR,
|
|
335
|
+
)
|
|
336
|
+
try:
|
|
337
|
+
top_k = max(1, min(int(top_k), 50))
|
|
338
|
+
except (TypeError, ValueError):
|
|
339
|
+
top_k = 5
|
|
340
|
+
try:
|
|
341
|
+
agent_name = _agent_name_from_context(context)
|
|
342
|
+
cwd = detect_cwd()
|
|
343
|
+
wings_to_search = _resolve_scope(wing, scope, agent_name, cwd)
|
|
344
|
+
hits = kennel.recent_drawers_multi(
|
|
345
|
+
wing_names=wings_to_search or None,
|
|
346
|
+
limit=top_k,
|
|
347
|
+
)
|
|
348
|
+
return KennelRecentOutput(
|
|
349
|
+
wings_searched=wings_to_search,
|
|
350
|
+
total=len(hits),
|
|
351
|
+
drawers=[_drawer_to_model(d) for d in hits],
|
|
352
|
+
)
|
|
353
|
+
except Exception as exc: # noqa: BLE001
|
|
354
|
+
return KennelRecentOutput(
|
|
355
|
+
wings_searched=[],
|
|
356
|
+
total=0,
|
|
357
|
+
error=f"kennel_recent failed: {exc!r}",
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
def register_kennel_list_wings(agent: Any) -> None:
|
|
362
|
+
"""Register the ``kennel_list_wings`` tool — wing discovery."""
|
|
363
|
+
|
|
364
|
+
@agent.tool
|
|
365
|
+
async def kennel_list_wings(context: RunContext) -> KennelWingsOutput:
|
|
366
|
+
"""List every wing in the kennel with its drawer count.
|
|
367
|
+
|
|
368
|
+
Use this to discover what memory partitions exist before scoping a
|
|
369
|
+
``kennel_recall`` or ``kennel_recent`` call.
|
|
370
|
+
"""
|
|
371
|
+
if not is_enabled():
|
|
372
|
+
return KennelWingsOutput(wings=[], total_wings=0, error=DISABLED_TOOL_ERROR)
|
|
373
|
+
try:
|
|
374
|
+
pairs = kennel.wings_with_counts()
|
|
375
|
+
return KennelWingsOutput(
|
|
376
|
+
wings=[WingInfo(name=n, drawer_count=c) for n, c in pairs],
|
|
377
|
+
total_wings=len(pairs),
|
|
378
|
+
)
|
|
379
|
+
except Exception as exc: # noqa: BLE001
|
|
380
|
+
return KennelWingsOutput(
|
|
381
|
+
wings=[],
|
|
382
|
+
total_wings=0,
|
|
383
|
+
error=f"kennel_list_wings failed: {exc!r}",
|
|
384
|
+
)
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
def register_kennel_stats(agent: Any) -> None:
|
|
388
|
+
"""Register the ``kennel_stats`` tool — kennel-wide totals + size."""
|
|
389
|
+
|
|
390
|
+
@agent.tool
|
|
391
|
+
async def kennel_stats(context: RunContext) -> KennelStatsOutput:
|
|
392
|
+
"""Return kennel-wide totals: drawers, wings, and on-disk size.
|
|
393
|
+
|
|
394
|
+
Useful for gating behavior — e.g. skip recall on an empty kennel.
|
|
395
|
+
"""
|
|
396
|
+
if not is_enabled():
|
|
397
|
+
return KennelStatsOutput(
|
|
398
|
+
db_path=str(DB_PATH),
|
|
399
|
+
db_size_bytes=0,
|
|
400
|
+
total_drawers=0,
|
|
401
|
+
total_wings=0,
|
|
402
|
+
error=DISABLED_TOOL_ERROR,
|
|
403
|
+
)
|
|
404
|
+
try:
|
|
405
|
+
size = DB_PATH.stat().st_size if DB_PATH.exists() else 0
|
|
406
|
+
return KennelStatsOutput(
|
|
407
|
+
db_path=str(DB_PATH),
|
|
408
|
+
db_size_bytes=size,
|
|
409
|
+
total_drawers=kennel.count_drawers(),
|
|
410
|
+
total_wings=len(kennel.list_wings()),
|
|
411
|
+
)
|
|
412
|
+
except Exception as exc: # noqa: BLE001
|
|
413
|
+
return KennelStatsOutput(
|
|
414
|
+
db_path=str(DB_PATH),
|
|
415
|
+
db_size_bytes=0,
|
|
416
|
+
total_drawers=0,
|
|
417
|
+
total_wings=0,
|
|
418
|
+
error=f"kennel_stats failed: {exc!r}",
|
|
419
|
+
)
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
def _agent_name_from_context(context: RunContext) -> str:
|
|
423
|
+
"""Best-effort extraction of the calling agent's name from the run context.
|
|
424
|
+
|
|
425
|
+
Falls back to ``"unknown"`` if the framework doesn't expose it on this
|
|
426
|
+
version of pydantic_ai.
|
|
427
|
+
"""
|
|
428
|
+
for attr in ("agent_name", "name"):
|
|
429
|
+
val = getattr(context, attr, None)
|
|
430
|
+
if val:
|
|
431
|
+
return str(val)
|
|
432
|
+
deps = getattr(context, "deps", None)
|
|
433
|
+
if deps is not None:
|
|
434
|
+
for attr in ("agent_name", "name"):
|
|
435
|
+
val = getattr(deps, attr, None)
|
|
436
|
+
if val:
|
|
437
|
+
return str(val)
|
|
438
|
+
return "unknown"
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
def register_tools_callback() -> list[dict[str, Any]]:
|
|
442
|
+
"""``register_tools`` callback — exposes the full kennel tool surface."""
|
|
443
|
+
return [
|
|
444
|
+
{"name": "kennel_recall", "register_func": register_kennel_recall},
|
|
445
|
+
{"name": "kennel_remember", "register_func": register_kennel_remember},
|
|
446
|
+
{"name": "kennel_recent", "register_func": register_kennel_recent},
|
|
447
|
+
{"name": "kennel_list_wings", "register_func": register_kennel_list_wings},
|
|
448
|
+
{"name": "kennel_stats", "register_func": register_kennel_stats},
|
|
449
|
+
]
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"""Wing namespacing — the convention that compartmentalizes memory.
|
|
2
|
+
|
|
3
|
+
Three namespaces, all stored in the same shared kennel:
|
|
4
|
+
|
|
5
|
+
* ``repo:<path>`` — project memory, shared between agents in that repo
|
|
6
|
+
* ``agent:<name>`` — per-agent diary, private by convention
|
|
7
|
+
* ``user:default`` — cross-cutting user preferences
|
|
8
|
+
|
|
9
|
+
Keep this module dependency-free; both recorder and retriever import it.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import os
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
USER_WING = "user:default"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _resolve_worktree_root(git_path: Path) -> Path | None:
|
|
21
|
+
"""Resolve main repo root from a worktree ``.git`` file.
|
|
22
|
+
|
|
23
|
+
Worktrees store ``.git`` as a file with a ``gitdir: ...`` pointer that
|
|
24
|
+
targets ``<main-repo>/.git/worktrees/<name>`` (absolute or relative).
|
|
25
|
+
This helper parses and resolves that pointer, then walks upward until it
|
|
26
|
+
finds the real ``.git`` directory and returns its parent repo root.
|
|
27
|
+
"""
|
|
28
|
+
try:
|
|
29
|
+
content = git_path.read_text(encoding="utf-8", errors="replace")
|
|
30
|
+
except (OSError, ValueError):
|
|
31
|
+
return None
|
|
32
|
+
|
|
33
|
+
gitdir_raw: str | None = None
|
|
34
|
+
for raw_line in content.splitlines():
|
|
35
|
+
line = raw_line.strip()
|
|
36
|
+
if line.lower().startswith("gitdir:"):
|
|
37
|
+
gitdir_raw = line[len("gitdir:") :].strip()
|
|
38
|
+
break
|
|
39
|
+
|
|
40
|
+
if not gitdir_raw:
|
|
41
|
+
return None
|
|
42
|
+
|
|
43
|
+
try:
|
|
44
|
+
gitdir_path = Path(gitdir_raw)
|
|
45
|
+
if not gitdir_path.is_absolute():
|
|
46
|
+
gitdir_path = git_path.parent / gitdir_path
|
|
47
|
+
gitdir_path = gitdir_path.resolve()
|
|
48
|
+
except (OSError, ValueError):
|
|
49
|
+
return None
|
|
50
|
+
|
|
51
|
+
# Resolve worktrees (``.git/worktrees``), not submodules (``.git/modules``);
|
|
52
|
+
# submodules are separate repos and keep their own wing.
|
|
53
|
+
if "worktrees" not in gitdir_path.parts:
|
|
54
|
+
return None
|
|
55
|
+
|
|
56
|
+
for parent in (gitdir_path, *gitdir_path.parents):
|
|
57
|
+
if parent.name == ".git" and parent.is_dir():
|
|
58
|
+
return parent.parent
|
|
59
|
+
|
|
60
|
+
return None
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def repo_wing(start: Path | str | None = None) -> str:
|
|
64
|
+
"""Return the ``repo:<path>`` wing for the given (or current) directory.
|
|
65
|
+
|
|
66
|
+
Walks upward looking for ``.git``. For normal repos, ``.git`` is a
|
|
67
|
+
directory and that candidate directory is used. For git worktrees,
|
|
68
|
+
``.git`` is a file; in that case this resolves the linked main repo root
|
|
69
|
+
and uses it so all worktrees share one repo wing. If none is found, falls
|
|
70
|
+
back to the resolved start directory itself — bare directories are still
|
|
71
|
+
legitimate project scopes.
|
|
72
|
+
"""
|
|
73
|
+
here = Path(start) if start else Path.cwd()
|
|
74
|
+
here = here.resolve()
|
|
75
|
+
for candidate in (here, *here.parents):
|
|
76
|
+
git_path = candidate / ".git"
|
|
77
|
+
if not git_path.exists():
|
|
78
|
+
continue
|
|
79
|
+
|
|
80
|
+
if git_path.is_file():
|
|
81
|
+
worktree_root = _resolve_worktree_root(git_path)
|
|
82
|
+
if worktree_root is not None:
|
|
83
|
+
return f"repo:{worktree_root}"
|
|
84
|
+
|
|
85
|
+
return f"repo:{candidate}"
|
|
86
|
+
|
|
87
|
+
return f"repo:{here}"
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def agent_wing(agent_name: str) -> str:
|
|
91
|
+
"""Return the ``agent:<name>`` wing for a given agent."""
|
|
92
|
+
safe = (agent_name or "unknown").strip() or "unknown"
|
|
93
|
+
return f"agent:{safe}"
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def default_recall_scope(agent_name: str, cwd: Path | str | None = None) -> list[str]:
|
|
97
|
+
"""The standard recall set: this repo + this agent + cross-user prefs."""
|
|
98
|
+
return [repo_wing(cwd), agent_wing(agent_name), USER_WING]
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def detect_cwd() -> Path:
|
|
102
|
+
"""Best-effort current working directory.
|
|
103
|
+
|
|
104
|
+
Falls back to ``$HOME`` if cwd is somehow unavailable (rare but possible
|
|
105
|
+
in some test environments).
|
|
106
|
+
"""
|
|
107
|
+
try:
|
|
108
|
+
return Path(os.getcwd())
|
|
109
|
+
except OSError:
|
|
110
|
+
return Path.home()
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# puppy_spinner
|
|
2
|
+
|
|
3
|
+
The bouncing-puppy spinner on the bottom bar's status-prefix slot —
|
|
4
|
+
now with customizable styles.
|
|
5
|
+
|
|
6
|
+
## `/spinner` command
|
|
7
|
+
|
|
8
|
+
| Command | What it does |
|
|
9
|
+
|---|---|
|
|
10
|
+
| `/spinner` | Interactive picker with a **live animated preview** |
|
|
11
|
+
| `/spinner <name> [seconds]` | Apply a spinner by name, optionally at a custom speed |
|
|
12
|
+
| `/spinner init` | Write a starter `spinners.json` for custom spinners |
|
|
13
|
+
|
|
14
|
+
Edits to `spinners.json` are picked up automatically (the file's mtime
|
|
15
|
+
is watched) — the currently animating spinner refreshes on its own,
|
|
16
|
+
and the picker reloads its list live *even while it's open*. No reload
|
|
17
|
+
step.
|
|
18
|
+
|
|
19
|
+
Picker navigation matches the `/plugins` menu: `j`/`k` + up/down move the
|
|
20
|
+
selection, `PgUp`/`PgDn` page through the list, `g`/`G` (or `Home`/`End`)
|
|
21
|
+
jump to first/last, `Enter` applies, `q`/`Esc`/`Ctrl-C` cancels.
|
|
22
|
+
`-`/`+` (or `←`/`→`) adjust the highlighted spinner's speed live in the
|
|
23
|
+
preview; `Enter` saves the speed you dialed straight into
|
|
24
|
+
`spinners.json` as that spinner's speed — same place you'd edit by
|
|
25
|
+
hand, and it sticks until you change it again. `i` writes the starter
|
|
26
|
+
`spinners.json` right from the menu (same as `/spinner init`).
|
|
27
|
+
|
|
28
|
+
The choice persists in `puppy.cfg` (`spinner_style` key) and takes
|
|
29
|
+
effect on the very next animation frame — no restart, even mid-run.
|
|
30
|
+
|
|
31
|
+
## Builtin spinners
|
|
32
|
+
|
|
33
|
+
Every builtin runs at 0.2s per frame by default — per-spinner tuning is
|
|
34
|
+
what the picker's speed keys and `spinners.json` tweaks are for. The
|
|
35
|
+
one exception: the classic `puppy` trots at 0.06s out of the box.
|
|
36
|
+
|
|
37
|
+
The puppy pack: `puppy` (default), `bone`, `zoomies`, `paws`, `dots`.
|
|
38
|
+
|
|
39
|
+
Plus a cli-spinners-style pack: `aesthetic`, `binary`, `bouncingBall`,
|
|
40
|
+
`bouncingBar`, `chevrons`, `dots8Bit`, `dotsCircle`, `dotsWide`,
|
|
41
|
+
`fistBump`, `growHorizontal`, `growVertical`, `noise`, `pong`, `sand`.
|
|
42
|
+
|
|
43
|
+
The picker shows everything alphabetically.
|
|
44
|
+
|
|
45
|
+
Frames for that pack live in `builtin_frames.py` — a generated,
|
|
46
|
+
data-only module (non-ASCII is escape-spelled for the repo emoji
|
|
47
|
+
filter); edit descriptions there, not frame art.
|
|
48
|
+
|
|
49
|
+
## Custom spinners
|
|
50
|
+
|
|
51
|
+
Drop a `spinners.json` next to `puppy.cfg` (usually
|
|
52
|
+
`~/.code_puppy/spinners.json` — the picker's preview pane shows the
|
|
53
|
+
exact path):
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"sniffer": {
|
|
58
|
+
"frames": ["( . ) ", "( . ) ", "( . ) ", "( . ) "],
|
|
59
|
+
"interval": 0.1,
|
|
60
|
+
"description": "a very minimalist puppy"
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Re-speeding a builtin
|
|
66
|
+
|
|
67
|
+
An entry that names a builtin and **omits `frames`** is a *tweak*: it
|
|
68
|
+
inherits the builtin's frames and just overrides `interval` and/or
|
|
69
|
+
`description` — no copying frame art:
|
|
70
|
+
|
|
71
|
+
```json
|
|
72
|
+
{
|
|
73
|
+
"zoomies": { "interval": 0.1 }
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Speed has one rule: a spinner runs at its `spinners.json` value when
|
|
78
|
+
present, else the builtin default (0.2s). The picker's speed keys and
|
|
79
|
+
`/spinner <name> <seconds>` don't add another layer — they just save
|
|
80
|
+
that value into the file for you.
|
|
81
|
+
|
|
82
|
+
Rules:
|
|
83
|
+
|
|
84
|
+
- `frames`: non-empty list of non-empty strings (padded to equal width
|
|
85
|
+
automatically; capped at 40 chars per frame). Omit it only when the
|
|
86
|
+
entry name matches a builtin (that's a tweak, see above).
|
|
87
|
+
- `interval`: seconds per frame, clamped to `0.02`–`1.0` (default `0.2`).
|
|
88
|
+
- `description`: optional, shown in the picker's preview pane.
|
|
89
|
+
- **User entries override builtins** on name collision — yes, you can
|
|
90
|
+
redefine `puppy`. We're not mad, just disappointed.
|
|
91
|
+
- A broken file never crashes anything: bad entries are skipped with a
|
|
92
|
+
log warning, and an unknown configured name falls back to the classic
|
|
93
|
+
kennel bounce.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""Puppy spinner plugin.
|
|
2
|
+
|
|
3
|
+
Brings back the beloved bouncing-puppy spinner from the pre-bottom-bar
|
|
4
|
+
era: while any agent run is in flight, the dog-face emoji bounces inside
|
|
5
|
+
its parenthesis kennel on the bottom bar's status-prefix slot, right
|
|
6
|
+
next to the token-context info.
|
|
7
|
+
|
|
8
|
+
Not a puppy person? ``/spinner`` opens a live-preview picker with a
|
|
9
|
+
kennel-ful of builtin styles, and ``spinners.json`` (next to
|
|
10
|
+
``puppy.cfg``) lets you define your own -- see ``/spinner init``.
|
|
11
|
+
"""
|