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,136 @@
|
|
|
1
|
+
# Skills System
|
|
2
|
+
|
|
3
|
+
> Part of the `code-puppy-agent` skill. Read this when working with
|
|
4
|
+
> skill discovery/activation, or extending how skills are presented to
|
|
5
|
+
> the model. See `SKILL.md` for the overview and the full reference map.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## How skills work
|
|
10
|
+
|
|
11
|
+
A skill is a directory containing a `SKILL.md` with YAML frontmatter
|
|
12
|
+
(`name`, `description`, optional `version`, `author`, `tags`) and
|
|
13
|
+
markdown body. The body is the instruction set loaded into context when
|
|
14
|
+
the skill is activated.
|
|
15
|
+
|
|
16
|
+
## Skill discovery (`plugins/agent_skills/discovery.py`)
|
|
17
|
+
|
|
18
|
+
Scans these directories (first-discovered wins on name collision):
|
|
19
|
+
1. `~/.code_puppy/skills/`
|
|
20
|
+
2. `<CWD>/.code_puppy/skills/`
|
|
21
|
+
3. `<CWD>/skills/`
|
|
22
|
+
4. Plugin-registered skills (via `register_skills` callback)
|
|
23
|
+
|
|
24
|
+
## Skill activation flow
|
|
25
|
+
|
|
26
|
+
1. The model calls `activate_skill("skill-name")` or the user types
|
|
27
|
+
`/<skill-name>`
|
|
28
|
+
2. The tool reads the full `SKILL.md` from disk
|
|
29
|
+
3. Content is injected into the model's context as a tool result
|
|
30
|
+
4. The model follows the instructions in its next response
|
|
31
|
+
|
|
32
|
+
Skills are **opt-in** — by default the model sees a one-line summary in
|
|
33
|
+
its system prompt (name + description) and must explicitly activate to
|
|
34
|
+
get full instructions. At small catalog sizes (a handful to a few dozen
|
|
35
|
+
skills) this flat list is fine. It stops scaling well before a few
|
|
36
|
+
hundred skills — see "Skill namespaces" below.
|
|
37
|
+
|
|
38
|
+
### Bundled resource files: filesystem skills vs. plugin-registered skills
|
|
39
|
+
|
|
40
|
+
`activate_skill` returns a `resources: List[str]` field listing files
|
|
41
|
+
bundled alongside `SKILL.md` (via `get_skill_resources()`), which the
|
|
42
|
+
model can then `read_file()` on demand instead of everything being
|
|
43
|
+
force-loaded on activation. This works cleanly for a **filesystem**
|
|
44
|
+
skill (a real directory under one of the scanned paths above) — any
|
|
45
|
+
sibling file just shows up.
|
|
46
|
+
|
|
47
|
+
It does **not** work the same way for a skill registered via the
|
|
48
|
+
`register_skills` callback with `skill_md_path` (like this skill,
|
|
49
|
+
`code-puppy-agent`, and `namespace_skill_search` originally considered
|
|
50
|
+
before dropping the idea): the discovery loader materializes only
|
|
51
|
+
`SKILL.md` itself into a cache directory (plus an optional `scripts_dir`
|
|
52
|
+
entry, and even that isn't surfaced through `get_skill_resources` since
|
|
53
|
+
it lands as a subdirectory, not a file). **If you want a plugin-owned
|
|
54
|
+
skill to reference sibling docs, put real repo paths in the SKILL.md
|
|
55
|
+
prose and let the model `read_file()` them directly** — don't rely on
|
|
56
|
+
the `resources` field to surface them. This document you're reading
|
|
57
|
+
right now is exactly that pattern: `SKILL.md` names this file by its
|
|
58
|
+
real path in the repo rather than expecting it to appear as a
|
|
59
|
+
`resources` entry.
|
|
60
|
+
|
|
61
|
+
## Managing skills
|
|
62
|
+
|
|
63
|
+
| Command | Effect |
|
|
64
|
+
|---------|--------|
|
|
65
|
+
| `/skills` | Interactive TUI menu |
|
|
66
|
+
| `/skills list` | Text list of all skills |
|
|
67
|
+
| `/skills enable` / `disable` | Toggle skills globally |
|
|
68
|
+
| `/skills frontmatter on/off` | Toggle skill summaries in system prompt |
|
|
69
|
+
| `/skills install` | Browse & install from remote catalog |
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Skill namespaces (large catalogs)
|
|
74
|
+
|
|
75
|
+
The **`namespace_skill_search`** builtin plugin
|
|
76
|
+
(`code_puppy/plugins/namespace_skill_search/`) addresses the
|
|
77
|
+
flat-list-doesn't-scale problem above. It's the reference
|
|
78
|
+
implementation to look at if you're extending how skills are surfaced
|
|
79
|
+
to the model, or building something in the same spirit for another
|
|
80
|
+
large catalog (tools, MCP servers, etc.).
|
|
81
|
+
|
|
82
|
+
**Shape:** groups skills into **namespaces** — each skill's namespace is
|
|
83
|
+
its first `tags:` entry (untagged skills fall back to a `General`
|
|
84
|
+
namespace) — and replaces the flat per-skill system-prompt list with a
|
|
85
|
+
compact namespace directory, plus a new `browse_skill_namespace` tool
|
|
86
|
+
with three modes (no-args directory listing, `namespace=` drill-down,
|
|
87
|
+
`query=` keyword search across everything). This mirrors OpenAI's
|
|
88
|
+
shipped `tool_search` + namespace-grouping pattern and Anthropic's
|
|
89
|
+
shipped Tool Search Tool, reimplemented in a model-agnostic way (no
|
|
90
|
+
provider-specific request fields) using only Code Puppy's existing
|
|
91
|
+
plugin hooks.
|
|
92
|
+
|
|
93
|
+
**User-facing docs:** `docs/AGENT_SKILLS.md` → "Skill Namespaces (Large
|
|
94
|
+
Catalogs)" section — what changes automatically, the tool's three
|
|
95
|
+
modes with examples, and how to opt back out
|
|
96
|
+
(`/plugins disable namespace_skill_search` + `/skills frontmatter on`).
|
|
97
|
+
|
|
98
|
+
**Design rationale + what was deliberately not built:**
|
|
99
|
+
`code_puppy/plugins/namespace_skill_search/README.md` — full writeup of
|
|
100
|
+
what was copied from OpenAI/Anthropic, why namespace-per-first-tag was
|
|
101
|
+
chosen over a vector DB or a deep multi-level tree, and the two hook-
|
|
102
|
+
system gotchas it was built to avoid (see `PLUGINS_AND_CALLBACKS.md` in
|
|
103
|
+
this reference set for those gotchas explained in general terms, not
|
|
104
|
+
tied to this one plugin).
|
|
105
|
+
|
|
106
|
+
**Implementation details worth knowing if you're extending it:**
|
|
107
|
+
- Namespace grouping is **case-insensitive at build time** — skills
|
|
108
|
+
tagged `finance`/`Finance`/`FINANCE` merge into one namespace, keyed
|
|
109
|
+
by first-seen casing. This isn't optional; the tool's `namespace=`
|
|
110
|
+
lookup is case-insensitive, so case-sensitive grouping would let two
|
|
111
|
+
namespaces silently fragment while looking like a single one from the
|
|
112
|
+
tool's perspective.
|
|
113
|
+
- A blank/whitespace-only `query` means "no filter," not "match
|
|
114
|
+
nothing" — a naive `any(term in haystack for term in
|
|
115
|
+
query.lower().split())` degrades to `any([])` (`False`) for every
|
|
116
|
+
skill on an empty query, which is a genuinely easy trap to fall into
|
|
117
|
+
with keyword-filter code in general, not specific to this plugin.
|
|
118
|
+
- Duplicate skill names across namespaces are flagged in the directory
|
|
119
|
+
output (not deduped or resolved) — `activate_skill(name)` has no way
|
|
120
|
+
to disambiguate between two skills sharing a name, and that's an
|
|
121
|
+
`agent_skills`-level gap this plugin surfaces more visibly rather than
|
|
122
|
+
causes.
|
|
123
|
+
|
|
124
|
+
## When to reach for `list_or_search_skills` / `activate_skill` vs. `browse_skill_namespace`
|
|
125
|
+
|
|
126
|
+
Both exist simultaneously and are not mutually exclusive:
|
|
127
|
+
- `list_or_search_skills` — the original, flat, always-available tool.
|
|
128
|
+
Fine for small catalogs or when you already roughly know the skill
|
|
129
|
+
name.
|
|
130
|
+
- `browse_skill_namespace` — better when the catalog is large enough
|
|
131
|
+
that `namespace_skill_search` has replaced the flat prompt block with
|
|
132
|
+
a namespace directory, and you want to browse by domain or search
|
|
133
|
+
across everything without scanning a huge flat list yourself.
|
|
134
|
+
|
|
135
|
+
Either way, the terminal step is the same: `activate_skill(name)` to
|
|
136
|
+
load the full instructions.
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# System Prompt Assembly, Configuration, Messaging & i18n
|
|
2
|
+
|
|
3
|
+
> Part of the `code-puppy-agent` skill. Read this when debugging what
|
|
4
|
+
> ends up in the system prompt (and in what order), working with
|
|
5
|
+
> `puppy.cfg`, emitting UI messages from a plugin, or wrapping
|
|
6
|
+
> user-facing strings for i18n. See `SKILL.md` for the overview and the
|
|
7
|
+
> full reference map.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## System Prompt Assembly
|
|
12
|
+
|
|
13
|
+
Assembled in two stages: `get_full_system_prompt()` builds the base
|
|
14
|
+
(`base_agent.py`), then `_assemble_instructions()` (`_builder.py`)
|
|
15
|
+
appends rules and runs the per-model patcher.
|
|
16
|
+
|
|
17
|
+
`get_full_system_prompt()` layers:
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
1. Authored prompt (agent.get_system_prompt())
|
|
21
|
+
2. load_prompt fragments (plugin-injected: kennel memory, permission
|
|
22
|
+
rules, namespace_skill_search's namespace directory, …)
|
|
23
|
+
3. Identity ID (agent name + UUID prefix)
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`_assemble_instructions()` then appends:
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
4. AGENTS.md puppy_rules (global ~/.code_puppy/AGENTS.md, then project
|
|
30
|
+
<CWD>/.code_puppy/AGENTS.md, then ./AGENTS.md fallback — concatenated,
|
|
31
|
+
not first-wins)
|
|
32
|
+
5. Extended-thinking note (only if active for the resolved model)
|
|
33
|
+
6. Per-model patches (get_model_system_prompt callbacks, via
|
|
34
|
+
prepare_prompt_for_model) — this is where the **skills block**
|
|
35
|
+
(available-skills summary) is appended, by the agent_skills plugin
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
> The skills summary is **not** a `load_prompt` fragment — it's injected
|
|
39
|
+
> through `get_model_system_prompt`, so it lands *after* identity and
|
|
40
|
+
> rules. **This is exactly the phase `namespace_skill_search` avoids
|
|
41
|
+
> registering a second callback on** — see `PLUGINS_AND_CALLBACKS.md`
|
|
42
|
+
> for why (`get_model_system_prompt` is last-write-wins across plugins,
|
|
43
|
+
> not additive like `load_prompt`).
|
|
44
|
+
|
|
45
|
+
Layers 2–6 are **runtime-only** — recomputed every run, never persisted
|
|
46
|
+
into static agent definitions. This prevents stale timestamps, dead file
|
|
47
|
+
paths, and baked-in session IDs from leaking into cloned agents.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Configuration System (`config.py`)
|
|
52
|
+
|
|
53
|
+
All settings live in `~/.code_puppy/puppy.cfg` (INI format) managed via:
|
|
54
|
+
- `get_value(key)` / `set_value(key, value)`
|
|
55
|
+
- `/set <key> <value>` slash command
|
|
56
|
+
|
|
57
|
+
Key directories:
|
|
58
|
+
- **Config root**: `~/.code_puppy/`
|
|
59
|
+
- **State/sessions**: `~/.code_puppy/state/` (or platform cache dir)
|
|
60
|
+
- **Cache**: `~/.code_puppy/cache/`
|
|
61
|
+
- **Agents**: `~/.code_puppy/agents/`
|
|
62
|
+
- **Skills**: `~/.code_puppy/skills/`
|
|
63
|
+
- **Plugins**: `~/.code_puppy/plugins/`
|
|
64
|
+
- **Extra models**: `~/.code_puppy/extra_models.json`
|
|
65
|
+
|
|
66
|
+
AGENTS.md rules files are loaded from `~/.code_puppy/AGENTS.md` (global),
|
|
67
|
+
`<CWD>/.code_puppy/AGENTS.md` (project), and `./AGENTS.md` (fallback).
|
|
68
|
+
|
|
69
|
+
### Disabling a plugin
|
|
70
|
+
|
|
71
|
+
`/plugins disable <name>` writes to the `disabled_plugins` config key
|
|
72
|
+
(JSON list) via `code_puppy/plugins/config.py`. A disabled plugin's
|
|
73
|
+
callbacks are skipped entirely — but any *persisted config change* the
|
|
74
|
+
plugin already made before being disabled (e.g. `namespace_skill_search`
|
|
75
|
+
turning off `frontmatter_in_system_prompt` on its first run) does **not**
|
|
76
|
+
automatically revert. Disabling the plugin and undoing what it already
|
|
77
|
+
did to your config are two separate actions.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Messaging & UI
|
|
82
|
+
|
|
83
|
+
### Message bus (`messaging/`)
|
|
84
|
+
|
|
85
|
+
Plugins emit UI messages through the message bus rather than printing
|
|
86
|
+
directly:
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
from code_puppy.messaging import emit_info, emit_success, emit_warning, emit_error
|
|
90
|
+
emit_info("Something happened")
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
These are rendered by the TUI's event handler, so they work in both
|
|
94
|
+
interactive and streaming contexts.
|
|
95
|
+
|
|
96
|
+
### Event streaming (`agents/event_stream_handler.py`)
|
|
97
|
+
|
|
98
|
+
The agent's streaming response is handled by `event_stream_handler`,
|
|
99
|
+
which processes pydantic-ai streaming events (text deltas, tool calls,
|
|
100
|
+
tool returns) and emits UI messages. The `stream_event` callback lets
|
|
101
|
+
plugins observe these events.
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Internationalization (i18n)
|
|
106
|
+
|
|
107
|
+
User-facing CLI/TUI text is localizable via the `code_puppy/i18n/` package
|
|
108
|
+
(stdlib-only; no Babel). **Full guide: `docs/I18N.md`.** Epic: PUP-473.
|
|
109
|
+
This section is a quick-reference cheat sheet, not a replacement for that
|
|
110
|
+
doc.
|
|
111
|
+
|
|
112
|
+
### Call sites
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
from code_puppy.i18n import t, ngettext, lazy
|
|
116
|
+
emit_info(t("startup.welcome", name=owner)) # simple, interpolated
|
|
117
|
+
emit_info(ngettext("files.deleted", count=n)) # plural-aware
|
|
118
|
+
emit_info(lazy("startup.ready")) # resolved at render time
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
- Keys are dotted IDs; **interpolation uses `{name}` only** — no f-strings,
|
|
122
|
+
no `str.format` on catalog text (attribute/index access and format specs are
|
|
123
|
+
deliberately unsupported; catalogs are untrusted input).
|
|
124
|
+
- A missing key echoes the key back — resolution **never raises**.
|
|
125
|
+
|
|
126
|
+
### Catalogs & locales
|
|
127
|
+
|
|
128
|
+
- JSON per locale: `i18n/locales/<locale>.json` (dotted-key → string, or a
|
|
129
|
+
plural dict `{"one": ..., "other": ...}`).
|
|
130
|
+
- Shipped: `en-US` (source), `es` (Latin American `es-419` folded in), and
|
|
131
|
+
`fr-CA`. Regional catalogs are added only when reviewed translations exist.
|
|
132
|
+
- **Fallback chain**: plain BCP-47 truncation, `es-AR → es → en-US` (see
|
|
133
|
+
`i18n.locale.fallback_chain`). `i18n.locale.PARENT_LOCALES` is a currently-
|
|
134
|
+
empty CLDR override seam for non-truncation parents. Plugins / the private
|
|
135
|
+
fork can register extra catalog dirs via `i18n.catalog.add_catalog_dir()`.
|
|
136
|
+
|
|
137
|
+
### How it wires in
|
|
138
|
+
|
|
139
|
+
- **Choke point**: `messaging/message_queue.py::emit_message` resolves a
|
|
140
|
+
`LazyTranslation` to a string; plain strings pass through unchanged.
|
|
141
|
+
- **Locale**: `config.get_locale()` is the single source of truth (delegates
|
|
142
|
+
to the i18n translator), seeded `CODE_PUPPY_LOCALE` › `locale` config key ›
|
|
143
|
+
POSIX env › `en-US`. `/set locale <tag>` persists it.
|
|
144
|
+
- **Formatting**: `i18n.format_number` / `i18n.format_datetime` (locale-aware).
|
|
145
|
+
|
|
146
|
+
### Rules
|
|
147
|
+
|
|
148
|
+
- **Model-facing system prompts are OUT of scope** — translating them changes
|
|
149
|
+
LLM behavior. Don't run them through the seam. (This is why
|
|
150
|
+
`namespace_skill_search`'s namespace-directory prompt fragment is plain,
|
|
151
|
+
un-wrapped English — it's model-facing content, not user-facing UI text.)
|
|
152
|
+
- Extraction goes behind the coverage/pseudolocale CI gate
|
|
153
|
+
(`tests/i18n/test_i18n_coverage.py`): every translated key must exist in the
|
|
154
|
+
`en-US` source, and a pseudolocale (`en-XA`) run must emit only bracketed
|
|
155
|
+
text (catches un-externalized strings).
|
|
156
|
+
- **Find what's left to extract** with the static audit:
|
|
157
|
+
`python -m code_puppy.i18n.audit --top 15` lists the worst files;
|
|
158
|
+
`--list` shows every raw `emit_*`/`console.print` site, `--json` is
|
|
159
|
+
machine-readable, and `--fail-under PCT` is a CI gate. Use it to pick the
|
|
160
|
+
next module, then the pseudolocale test above to prove it's fully migrated.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Register the built-in ``code-puppy-agent`` skill.
|
|
2
|
+
|
|
3
|
+
The skill's SKILL.md lives alongside this file and is an *index* into a
|
|
4
|
+
set of topic-specific reference files (``AGENTS_AND_TOOLS.md``,
|
|
5
|
+
``PLUGINS_AND_CALLBACKS.md``, ``MODELS_AND_MCP.md``,
|
|
6
|
+
``SESSIONS_AND_HISTORY.md``, ``SKILLS_SYSTEM.md``,
|
|
7
|
+
``SYSTEM_PROMPT_CONFIG_AND_I18N.md``) in the same directory. This keeps
|
|
8
|
+
any single activation cheap while still covering the full internals
|
|
9
|
+
surface -- see SKILL.md's "Reference Map" for which file answers which
|
|
10
|
+
question.
|
|
11
|
+
|
|
12
|
+
We register it via the ``register_skills`` callback so the agent_skills
|
|
13
|
+
plugin materializes it into the plugin-skill cache and it shows up in
|
|
14
|
+
``/skills list``, ``activate_skill``, and the system-prompt skill block --
|
|
15
|
+
just like any user-installed skill.
|
|
16
|
+
|
|
17
|
+
Note: only ``skill_md_path`` is registered below, not the reference
|
|
18
|
+
files. Plugin-registered skills (``skill_md_path``/``skill_md``/
|
|
19
|
+
``frontmatter``+``body``) only ever materialize ``SKILL.md`` itself into
|
|
20
|
+
the runtime cache -- sibling files are NOT auto-discovered as
|
|
21
|
+
``resources`` the way they would be for a real filesystem skill
|
|
22
|
+
directory. That's why SKILL.md references the other files by their real
|
|
23
|
+
repo paths in prose (for the model to ``read_file`` on demand) rather
|
|
24
|
+
than relying on the ``resources`` field ``activate_skill`` returns.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
from pathlib import Path
|
|
28
|
+
|
|
29
|
+
from code_puppy.callbacks import register_callback
|
|
30
|
+
|
|
31
|
+
_SKILL_DIR = Path(__file__).resolve().parent
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _register_builtin_skills() -> list[dict]:
|
|
35
|
+
# This "name" MUST match the SKILL.md frontmatter ``name:``: it drives dedup +
|
|
36
|
+
# the disable-set, while frontmatter name drives display; a mismatch would
|
|
37
|
+
# silently break ``/skills enable|disable`` and the alias.
|
|
38
|
+
return [
|
|
39
|
+
{
|
|
40
|
+
"name": "code-puppy-agent",
|
|
41
|
+
"skill_md_path": str(_SKILL_DIR / "SKILL.md"),
|
|
42
|
+
}
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
register_callback("register_skills", _register_builtin_skills)
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import AppKit
|
|
2
|
+
import CoreGraphics
|
|
3
|
+
import Foundation
|
|
4
|
+
import ScreenCaptureKit
|
|
5
|
+
|
|
6
|
+
enum CaptureFailure: LocalizedError {
|
|
7
|
+
case message(String)
|
|
8
|
+
|
|
9
|
+
var errorDescription: String? {
|
|
10
|
+
switch self {
|
|
11
|
+
case .message(let text): return text
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
@main
|
|
16
|
+
struct NativeCapture {
|
|
17
|
+
static func scale(for frame: CGRect) -> CGFloat {
|
|
18
|
+
let midpoint = CGPoint(x: frame.midX, y: frame.midY)
|
|
19
|
+
for screen in NSScreen.screens {
|
|
20
|
+
guard
|
|
21
|
+
let number = screen.deviceDescription[
|
|
22
|
+
NSDeviceDescriptionKey("NSScreenNumber")
|
|
23
|
+
] as? NSNumber
|
|
24
|
+
else { continue }
|
|
25
|
+
let bounds = CGDisplayBounds(CGDirectDisplayID(number.uint32Value))
|
|
26
|
+
if bounds.contains(midpoint) {
|
|
27
|
+
return screen.backingScaleFactor
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return NSScreen.main?.backingScaleFactor ?? 1
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
static func matchingApplication(_ query: String) throws -> NSRunningApplication {
|
|
34
|
+
let wanted = query.lowercased()
|
|
35
|
+
guard let app = NSWorkspace.shared.runningApplications.first(where: {
|
|
36
|
+
($0.localizedName ?? "").lowercased() == wanted
|
|
37
|
+
|| ($0.bundleIdentifier ?? "").lowercased() == wanted
|
|
38
|
+
}) else {
|
|
39
|
+
throw CaptureFailure.message("Running application not found: \(query)")
|
|
40
|
+
}
|
|
41
|
+
return app
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
static func run(appName: String, outputPath: String) async throws -> [String: Any] {
|
|
45
|
+
let app = try matchingApplication(appName)
|
|
46
|
+
let content = try await SCShareableContent.excludingDesktopWindows(
|
|
47
|
+
false,
|
|
48
|
+
onScreenWindowsOnly: false
|
|
49
|
+
)
|
|
50
|
+
let candidates = content.windows.filter {
|
|
51
|
+
guard $0.owningApplication?.processID == app.processIdentifier else {
|
|
52
|
+
return false
|
|
53
|
+
}
|
|
54
|
+
return $0.frame.width >= 200 && $0.frame.height >= 120
|
|
55
|
+
}
|
|
56
|
+
guard let window = candidates.max(by: {
|
|
57
|
+
$0.frame.width * $0.frame.height < $1.frame.width * $1.frame.height
|
|
58
|
+
}) else {
|
|
59
|
+
throw CaptureFailure.message("No capturable window found for \(appName).")
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
let scale = scale(for: window.frame)
|
|
63
|
+
let configuration = SCStreamConfiguration()
|
|
64
|
+
configuration.width = max(1, Int((window.frame.width * scale).rounded()))
|
|
65
|
+
configuration.height = max(1, Int((window.frame.height * scale).rounded()))
|
|
66
|
+
configuration.showsCursor = false
|
|
67
|
+
configuration.scalesToFit = false
|
|
68
|
+
let filter = SCContentFilter(desktopIndependentWindow: window)
|
|
69
|
+
let image = try await SCScreenshotManager.captureImage(
|
|
70
|
+
contentFilter: filter,
|
|
71
|
+
configuration: configuration
|
|
72
|
+
)
|
|
73
|
+
let representation = NSBitmapImageRep(cgImage: image)
|
|
74
|
+
guard let data = representation.representation(
|
|
75
|
+
using: .png,
|
|
76
|
+
properties: [:]
|
|
77
|
+
) else {
|
|
78
|
+
throw CaptureFailure.message("Could not encode screenshot as PNG.")
|
|
79
|
+
}
|
|
80
|
+
try data.write(to: URL(fileURLWithPath: outputPath), options: .atomic)
|
|
81
|
+
|
|
82
|
+
return [
|
|
83
|
+
"application": app.localizedName ?? appName,
|
|
84
|
+
"bundle_id": app.bundleIdentifier ?? "",
|
|
85
|
+
"pid": Int(app.processIdentifier),
|
|
86
|
+
"window_id": Int(window.windowID),
|
|
87
|
+
"window_title": window.title ?? "",
|
|
88
|
+
"backing_scale": Double(scale),
|
|
89
|
+
"window_bounds_points": [
|
|
90
|
+
"x": Double(window.frame.origin.x),
|
|
91
|
+
"y": Double(window.frame.origin.y),
|
|
92
|
+
"width": Double(window.frame.width),
|
|
93
|
+
"height": Double(window.frame.height),
|
|
94
|
+
],
|
|
95
|
+
"screenshot_size_pixels": [
|
|
96
|
+
"width": configuration.width,
|
|
97
|
+
"height": configuration.height,
|
|
98
|
+
],
|
|
99
|
+
]
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
static func main() async {
|
|
103
|
+
guard CommandLine.arguments.count == 3 else {
|
|
104
|
+
FileHandle.standardError.write(
|
|
105
|
+
Data("usage: native-capture APP OUTPUT.png\n".utf8)
|
|
106
|
+
)
|
|
107
|
+
exit(2)
|
|
108
|
+
}
|
|
109
|
+
do {
|
|
110
|
+
let payload = try await run(
|
|
111
|
+
appName: CommandLine.arguments[1],
|
|
112
|
+
outputPath: CommandLine.arguments[2]
|
|
113
|
+
)
|
|
114
|
+
let data = try JSONSerialization.data(withJSONObject: payload)
|
|
115
|
+
FileHandle.standardOutput.write(data)
|
|
116
|
+
} catch {
|
|
117
|
+
FileHandle.standardError.write(
|
|
118
|
+
Data("\(error.localizedDescription)\n".utf8)
|
|
119
|
+
)
|
|
120
|
+
exit(1)
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# macOS Computer Use
|
|
2
|
+
|
|
3
|
+
This opt-in plugin lets Code Puppy inspect and operate the current macOS desktop
|
|
4
|
+
through Apple's Accessibility APIs. It is macOS-only and does not use a virtual
|
|
5
|
+
machine or copy any Codex implementation.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install "code-puppy[computer-use]"
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
On first use, macOS requests permissions for the terminal or application that
|
|
14
|
+
runs Code Puppy:
|
|
15
|
+
|
|
16
|
+
1. **Accessibility** for inspecting and operating controls.
|
|
17
|
+
2. **Screen Recording** for screenshots.
|
|
18
|
+
|
|
19
|
+
Grant these under **System Settings → Privacy & Security**. Restart Code Puppy
|
|
20
|
+
after changing Screen Recording access.
|
|
21
|
+
|
|
22
|
+
Computer Use is off by default: its tools are not exposed to the model until
|
|
23
|
+
you explicitly opt in. On macOS, Code Puppy shows a launch reminder with the
|
|
24
|
+
enable command. The choice is stored in the normal Code Puppy settings directory:
|
|
25
|
+
|
|
26
|
+
```text
|
|
27
|
+
/computer-use enable
|
|
28
|
+
/computer-use disable
|
|
29
|
+
/computer-use status
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
You can change this setting at any time. Once enabled, Computer Use runs
|
|
33
|
+
without per-application approval prompts or an allowlist.
|
|
34
|
+
|
|
35
|
+
Use `/computer-use pause` as an emergency stop and `/computer-use resume` only
|
|
36
|
+
after confirming it is safe. `/computer-use deny BUNDLE_ID` persists an
|
|
37
|
+
exception, and `/computer-use allow BUNDLE_ID` removes that exception. Login,
|
|
38
|
+
authorization, Keychain, and System Settings processes are always blocked.
|
|
39
|
+
|
|
40
|
+
Snapshots, explicit screenshots, and successful batches display a compact
|
|
41
|
+
inline preview in Ghostty, Kitty, WezTerm, and iTerm2. The same PNG is attached
|
|
42
|
+
to the tool result so multimodal models can inspect it directly. App-targeted
|
|
43
|
+
snapshots capture that application's largest standard window even when another
|
|
44
|
+
application is in front.
|
|
45
|
+
|
|
46
|
+
## Agent workflow
|
|
47
|
+
|
|
48
|
+
1. Call `computer_get_app_state` with an application name.
|
|
49
|
+
2. Use its screenshot, pruned focused-window accessibility tree, and
|
|
50
|
+
`state_revision` to choose an action. Prefer an element ID and one of the
|
|
51
|
+
element's advertised actions.
|
|
52
|
+
3. Run one mutation, or one guarded batch of at most 20 mutations.
|
|
53
|
+
4. Fetch fresh state. A completed batch does this automatically after waiting
|
|
54
|
+
for the accessibility tree to stabilize.
|
|
55
|
+
|
|
56
|
+
Before every mutation, the target process is activated and verified as the
|
|
57
|
+
frontmost application. This is required because ScreenCaptureKit can capture a
|
|
58
|
+
background window, while Quartz sends global clicks and keystrokes to whichever
|
|
59
|
+
application is actually frontmost. Use a guarded batch for click-then-type
|
|
60
|
+
workflows so focus is retained and the result is verified in one control loop.
|
|
61
|
+
|
|
62
|
+
Pixel clicks and drags use window-local screenshot pixels. The backend converts
|
|
63
|
+
them to global Quartz points using the captured window bounds, so Retina scaling,
|
|
64
|
+
negative display origins, and windows on secondary displays remain consistent.
|
|
65
|
+
Scrolling accepts `up`, `down`, `left`, or `right` and a fractional page count.
|
|
66
|
+
Text selection supports an exact match, optional prefix/suffix disambiguation,
|
|
67
|
+
and cursor placement before or after the match.
|
|
68
|
+
|
|
69
|
+
## Safety
|
|
70
|
+
|
|
71
|
+
- Prefer accessibility element IDs over pixel coordinates.
|
|
72
|
+
- Persisted user consent is required before tools are exposed to the model;
|
|
73
|
+
disabling the feature removes them again and blocks future captures/actions.
|
|
74
|
+
- State revisions and element IDs are single-use and expire after 120 seconds.
|
|
75
|
+
- Physical keyboard and pointer input do not interrupt YOLO-mode execution.
|
|
76
|
+
- Every capture and mutation re-checks the application policy; the emergency
|
|
77
|
+
stop therefore also interrupts a batch at its next action boundary.
|
|
78
|
+
- Password-field values are never returned in snapshots.
|
|
79
|
+
- Batches stop at the first failed action, contain at most 20 steps, wait for
|
|
80
|
+
deterministic UI stability, and return updated screenshot/tree state.
|
|
81
|
+
- The plugin does not pause for consequential-action confirmations in YOLO
|
|
82
|
+
mode. Use `/computer-use pause` when autonomous interaction should stop.
|
|
83
|
+
|
|
84
|
+
## Native capture and privacy
|
|
85
|
+
|
|
86
|
+
Application screenshots use ScreenCaptureKit through the bundled Swift helper.
|
|
87
|
+
The helper is compiled locally, cached with a source hash, and returns stable
|
|
88
|
+
window metadata: bundle ID, PID, window ID/title, bounds, pixel dimensions, and
|
|
89
|
+
backing scale. Screenshot PNGs are temporary local files. This plugin does not
|
|
90
|
+
add telemetry, persistent action history, lock-screen automation, or privileged
|
|
91
|
+
system interaction.
|
|
92
|
+
|
|
93
|
+
The first native capture requires Xcode Command Line Tools because the bundled
|
|
94
|
+
helper is compiled on the Mac:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
xcode-select --install
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Test
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
uv run ruff check code_puppy/plugins/computer_use tests/plugins
|
|
104
|
+
uv run pytest tests/plugins/test_computer_use_*.py -q --no-cov
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
For a live Ghostty smoke test, launch Code Puppy from Ghostty and ask for
|
|
108
|
+
`computer_get_app_state`. A compact image should appear inline; the tool result
|
|
109
|
+
still carries the PNG if the terminal protocol is unavailable.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Opt-in macOS computer-use tools."""
|