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,197 @@
|
|
|
1
|
+
"""`/statusline` command — scaffold, enable, inspect the status line.
|
|
2
|
+
|
|
3
|
+
Mirrors Claude Code's ``/statusline``. Rather than calling the model, this
|
|
4
|
+
scaffolds a ready-to-edit starter script (Claude Code generates one via the
|
|
5
|
+
agent; we hand you a working template you can tweak immediately).
|
|
6
|
+
|
|
7
|
+
Subcommands:
|
|
8
|
+
/statusline Show current status + quick help
|
|
9
|
+
/statusline init Write a starter ~/.code_puppy/statusline.sh,
|
|
10
|
+
point config at it, and enable
|
|
11
|
+
/statusline on | off Enable / disable rendering
|
|
12
|
+
/statusline show Run the command once and preview its output
|
|
13
|
+
/statusline json Print the JSON payload your script receives
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import stat
|
|
19
|
+
import subprocess as _sp
|
|
20
|
+
import sys
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
from typing import Any, List, Optional, Tuple
|
|
23
|
+
|
|
24
|
+
from code_puppy.messaging import emit_info, emit_success, emit_warning
|
|
25
|
+
|
|
26
|
+
from . import config, payload, runner
|
|
27
|
+
|
|
28
|
+
_COMMAND_NAME = "statusline"
|
|
29
|
+
|
|
30
|
+
# PowerShell equivalent for Windows — no bash, no jq required.
|
|
31
|
+
_STARTER_SCRIPT_PS1 = """\
|
|
32
|
+
# Code Puppy status line — PowerShell version.
|
|
33
|
+
# Receives session JSON on stdin; prints one line to stdout.
|
|
34
|
+
# Edit freely. Requires PowerShell 5+ (ships with Windows 10+).
|
|
35
|
+
$input_text = $input | Out-String
|
|
36
|
+
try {
|
|
37
|
+
$data = $input_text | ConvertFrom-Json
|
|
38
|
+
} catch {
|
|
39
|
+
$data = [PSCustomObject]@{}
|
|
40
|
+
}
|
|
41
|
+
$puppy = if ($data.puppy_name) { $data.puppy_name } else { "code-puppy" }
|
|
42
|
+
$model = if ($data.model.display_name) { $data.model.display_name } else { "model" }
|
|
43
|
+
$dir = if ($data.workspace.current_dir) { $data.workspace.current_dir } elseif ($data.cwd) { $data.cwd } else { "" }
|
|
44
|
+
$branch = if ($data.workspace.git_branch) { " ($($data.workspace.git_branch))" } else { "" }
|
|
45
|
+
$pct = if ($null -ne $data.context_window.used_percentage) { $data.context_window.used_percentage } else { 0 }
|
|
46
|
+
$base = if ($dir) { Split-Path $dir -Leaf } else { "" }
|
|
47
|
+
Write-Output "🐶 $puppy [$model] $base$branch ${pct}%ctx" # stdout, captured by parent process
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
_STARTER_SCRIPT = """\
|
|
51
|
+
#!/usr/bin/env bash
|
|
52
|
+
# Code Puppy status line. Receives session JSON on stdin; prints one line.
|
|
53
|
+
# In the default "replace" mode this line REPLACES the default prompt content
|
|
54
|
+
# (so include a name/emoji if you want one). Edit freely.
|
|
55
|
+
# Requires `jq` for the parsing below (or parse however you like).
|
|
56
|
+
input=$(cat)
|
|
57
|
+
|
|
58
|
+
puppy=$(printf '%s' "$input" | jq -r '.puppy_name // "code-puppy"')
|
|
59
|
+
model=$(printf '%s' "$input" | jq -r '.model.display_name // "model"')
|
|
60
|
+
dir=$(printf '%s' "$input" | jq -r '.workspace.current_dir // .cwd // ""')
|
|
61
|
+
branch=$(printf '%s' "$input" | jq -r '.workspace.git_branch // empty')
|
|
62
|
+
ind=$(printf '%s' "$input" | jq -r '.context_window.indicator // ""')
|
|
63
|
+
pct=$(printf '%s' "$input" | jq -r '.context_window.used_percentage // 0')
|
|
64
|
+
tps=$(printf '%s' "$input" | jq -r '.tokens_per_second // 0')
|
|
65
|
+
|
|
66
|
+
line="\\033[1m🐶 $puppy\\033[0m"
|
|
67
|
+
[ -n "$ind" ] && line="$line $ind"
|
|
68
|
+
line="$line \\033[36m[$model]\\033[0m \\033[2m$(basename "$dir")\\033[0m"
|
|
69
|
+
[ -n "$branch" ] && line="$line \\033[35m($branch)\\033[0m"
|
|
70
|
+
line="$line \\033[33m${pct}%ctx\\033[0m"
|
|
71
|
+
# Show t/s only while generating.
|
|
72
|
+
awk "BEGIN{exit !($tps>0)}" && line="$line \\033[32m${tps} t/s\\033[0m"
|
|
73
|
+
|
|
74
|
+
printf '%b' "$line"
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def statusline_command_help() -> List[Tuple[str, str]]:
|
|
79
|
+
return [
|
|
80
|
+
(_COMMAND_NAME, "Customize the bottom status line (init/on/off/show/json)"),
|
|
81
|
+
]
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _default_script_path() -> Path:
|
|
85
|
+
if sys.platform == "win32":
|
|
86
|
+
return Path.home() / ".code_puppy" / "statusline.ps1"
|
|
87
|
+
return Path.home() / ".code_puppy" / "statusline.sh"
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _status_text() -> str:
|
|
91
|
+
enabled = "ON" if config.is_enabled() else "OFF"
|
|
92
|
+
cmd = config.get_command() or "(none)"
|
|
93
|
+
return (
|
|
94
|
+
f"Status line: {enabled} mode: {config.get_mode()}\n"
|
|
95
|
+
f" command: {cmd}\n"
|
|
96
|
+
f" refresh: {config.get_refresh_ms()}ms timeout: {config.get_timeout_ms()}ms\n"
|
|
97
|
+
" Subcommands: init | on | off | mode <replace|above|newline> | show | json"
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _do_init() -> None:
|
|
102
|
+
path = _default_script_path()
|
|
103
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
104
|
+
if sys.platform == "win32":
|
|
105
|
+
# Windows: write a PowerShell script; invoke via powershell.exe so no
|
|
106
|
+
# bash/jq dependency and no chmod needed.
|
|
107
|
+
path.write_text(_STARTER_SCRIPT_PS1, encoding="utf-8")
|
|
108
|
+
# list2cmdline handles cmd.exe quoting rules for paths with spaces
|
|
109
|
+
ps1_cmd = _sp.list2cmdline(
|
|
110
|
+
["powershell", "-ExecutionPolicy", "Bypass", "-File", str(path)]
|
|
111
|
+
)
|
|
112
|
+
config.set_command(ps1_cmd)
|
|
113
|
+
runner.reset_cache()
|
|
114
|
+
emit_success(f"Wrote starter status line script: {path}")
|
|
115
|
+
emit_info(
|
|
116
|
+
"Enabled. Edit that file to customize. Preview with /statusline show."
|
|
117
|
+
)
|
|
118
|
+
else:
|
|
119
|
+
path.write_text(_STARTER_SCRIPT, encoding="utf-8")
|
|
120
|
+
path.chmod(path.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
|
|
121
|
+
config.set_command(str(path))
|
|
122
|
+
runner.reset_cache()
|
|
123
|
+
emit_success(f"Wrote starter status line script: {path}")
|
|
124
|
+
emit_info(
|
|
125
|
+
"Enabled. Edit that file to customize. Preview with /statusline show."
|
|
126
|
+
)
|
|
127
|
+
if not _has_jq():
|
|
128
|
+
emit_warning(
|
|
129
|
+
"Note: the starter script uses `jq` (not found on PATH). Install jq "
|
|
130
|
+
"or rewrite the script to parse JSON another way."
|
|
131
|
+
)
|
|
132
|
+
config.set_enabled(True)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _has_jq() -> bool:
|
|
136
|
+
from shutil import which
|
|
137
|
+
|
|
138
|
+
return which("jq") is not None
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def handle_statusline_command(command: str, name: str) -> Optional[Any]:
|
|
142
|
+
if name != _COMMAND_NAME:
|
|
143
|
+
return None
|
|
144
|
+
|
|
145
|
+
tokens = command.split()
|
|
146
|
+
sub = tokens[1].strip().lower() if len(tokens) > 1 else "status"
|
|
147
|
+
|
|
148
|
+
if sub in ("status", ""):
|
|
149
|
+
emit_info(_status_text())
|
|
150
|
+
return True
|
|
151
|
+
if sub == "init":
|
|
152
|
+
_do_init()
|
|
153
|
+
return True
|
|
154
|
+
if sub == "on":
|
|
155
|
+
if not config.get_command():
|
|
156
|
+
emit_warning(
|
|
157
|
+
"No command set. Run /statusline init first, or set "
|
|
158
|
+
"statusline_command via /set."
|
|
159
|
+
)
|
|
160
|
+
return True
|
|
161
|
+
config.set_enabled(True)
|
|
162
|
+
runner.reset_cache()
|
|
163
|
+
emit_success("Status line enabled.")
|
|
164
|
+
return True
|
|
165
|
+
if sub == "off":
|
|
166
|
+
config.set_enabled(False)
|
|
167
|
+
emit_warning("Status line disabled.")
|
|
168
|
+
return True
|
|
169
|
+
if sub == "mode":
|
|
170
|
+
mode = tokens[2].strip().lower() if len(tokens) > 2 else ""
|
|
171
|
+
if mode not in ("replace", "above", "newline"):
|
|
172
|
+
emit_warning("Usage: /statusline mode <replace|above|newline>")
|
|
173
|
+
emit_info(
|
|
174
|
+
" replace = your line REPLACES the default prompt (no duplicate)\n"
|
|
175
|
+
" above = your line sits on its own line above the default prompt\n"
|
|
176
|
+
" newline = like replace, but >>> drops to its own line below"
|
|
177
|
+
)
|
|
178
|
+
return True
|
|
179
|
+
config.set_mode(mode)
|
|
180
|
+
emit_success(f"Status line mode set to '{mode}'.")
|
|
181
|
+
return True
|
|
182
|
+
if sub == "show":
|
|
183
|
+
if not config.get_command():
|
|
184
|
+
emit_warning("No command set. Run /statusline init first.")
|
|
185
|
+
return True
|
|
186
|
+
out = runner.run_once_sync()
|
|
187
|
+
emit_info("Status line preview (raw output):")
|
|
188
|
+
emit_info(out or "(empty)")
|
|
189
|
+
return True
|
|
190
|
+
if sub == "json":
|
|
191
|
+
emit_info("JSON payload your status command receives on stdin:")
|
|
192
|
+
emit_info(payload.build_payload_json())
|
|
193
|
+
return True
|
|
194
|
+
|
|
195
|
+
emit_warning(f"Unknown /statusline subcommand: {sub}")
|
|
196
|
+
emit_info("Use: init | on | off | show | json")
|
|
197
|
+
return True
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""Steer/queue plugin.
|
|
2
|
+
|
|
3
|
+
Mid-run submissions now QUEUE by default (run as the next turn); this
|
|
4
|
+
plugin provides the escape hatches and the management UI:
|
|
5
|
+
|
|
6
|
+
* ``/steer <text>`` -- inject guidance mid-turn (the old default).
|
|
7
|
+
* ``/queue`` -- full-screen TUI to view, add, edit, reorder, and delete prompts,
|
|
8
|
+
available at idle and mid-run alike.
|
|
9
|
+
* ``(N queued)`` -- live count on the bottom bar's status row.
|
|
10
|
+
"""
|
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
"""Full-screen queue manager for ``/queue``.
|
|
2
|
+
|
|
3
|
+
The application owns presentation state only. Queue mutations continue to flow
|
|
4
|
+
through :class:`PauseController`, keeping listeners and the bottom status bar in
|
|
5
|
+
sync with edits made here.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import logging
|
|
11
|
+
from dataclasses import dataclass
|
|
12
|
+
from typing import List, Optional
|
|
13
|
+
from code_puppy.callbacks import on_prompt_toolkit_style
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
_PREVIEW_CELLS = 58
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _preview(text: str, width: int = _PREVIEW_CELLS) -> str:
|
|
21
|
+
"""Return a compact single-line preview for a queued prompt."""
|
|
22
|
+
flat = " ".join(text.split())
|
|
23
|
+
if len(flat) <= width:
|
|
24
|
+
return flat
|
|
25
|
+
return flat[: max(0, width - 1)] + "…"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass
|
|
29
|
+
class QueueMenuState:
|
|
30
|
+
"""Small, testable state adapter around ``PauseController``."""
|
|
31
|
+
|
|
32
|
+
controller: object
|
|
33
|
+
selected: int = 0
|
|
34
|
+
editing: bool = False
|
|
35
|
+
adding: bool = False
|
|
36
|
+
delete_armed: Optional[int] = None
|
|
37
|
+
notice: str = ""
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
def items(self) -> List[str]:
|
|
41
|
+
return self.controller.peek_pending_steer_queued()
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def selected_text(self) -> str:
|
|
45
|
+
items = self.items
|
|
46
|
+
if not items:
|
|
47
|
+
return ""
|
|
48
|
+
self.selected = min(max(self.selected, 0), len(items) - 1)
|
|
49
|
+
return items[self.selected]
|
|
50
|
+
|
|
51
|
+
def clamp_selection(self) -> None:
|
|
52
|
+
self.selected = min(max(self.selected, 0), max(0, len(self.items) - 1))
|
|
53
|
+
|
|
54
|
+
def move_selection(self, delta: int) -> None:
|
|
55
|
+
if not self.items:
|
|
56
|
+
self.selected = 0
|
|
57
|
+
return
|
|
58
|
+
self.selected = min(max(self.selected + delta, 0), len(self.items) - 1)
|
|
59
|
+
self.delete_armed = None
|
|
60
|
+
self.notice = ""
|
|
61
|
+
|
|
62
|
+
def begin_add(self) -> None:
|
|
63
|
+
self.editing = True
|
|
64
|
+
self.adding = True
|
|
65
|
+
self.delete_armed = None
|
|
66
|
+
self.notice = "Adding prompt — Ctrl+S save · Esc cancel"
|
|
67
|
+
|
|
68
|
+
def begin_edit(self) -> bool:
|
|
69
|
+
if not self.items:
|
|
70
|
+
self.notice = "Queue is empty — press A to add a prompt"
|
|
71
|
+
return False
|
|
72
|
+
self.editing = True
|
|
73
|
+
self.adding = False
|
|
74
|
+
self.delete_armed = None
|
|
75
|
+
self.notice = "Editing prompt — Ctrl+S save · Esc cancel"
|
|
76
|
+
return True
|
|
77
|
+
|
|
78
|
+
def cancel_edit(self) -> None:
|
|
79
|
+
self.editing = False
|
|
80
|
+
self.adding = False
|
|
81
|
+
self.notice = "Edit cancelled"
|
|
82
|
+
|
|
83
|
+
def save(self, text: str) -> bool:
|
|
84
|
+
normalized = text.strip()
|
|
85
|
+
if not normalized:
|
|
86
|
+
self.notice = "Prompt cannot be blank"
|
|
87
|
+
return False
|
|
88
|
+
|
|
89
|
+
items = self.items
|
|
90
|
+
if self.adding:
|
|
91
|
+
self.controller.request_steer(normalized, mode="queue")
|
|
92
|
+
self.selected = len(items)
|
|
93
|
+
self.notice = "Prompt added"
|
|
94
|
+
elif self.selected < len(items):
|
|
95
|
+
items[self.selected] = normalized
|
|
96
|
+
self.controller.replace_pending_steer_queued(items)
|
|
97
|
+
self.notice = "Prompt updated"
|
|
98
|
+
else:
|
|
99
|
+
self.notice = "That queue item no longer exists"
|
|
100
|
+
return False
|
|
101
|
+
|
|
102
|
+
self.editing = False
|
|
103
|
+
self.adding = False
|
|
104
|
+
self.delete_armed = None
|
|
105
|
+
self.clamp_selection()
|
|
106
|
+
return True
|
|
107
|
+
|
|
108
|
+
def request_delete(self) -> bool:
|
|
109
|
+
if not self.items:
|
|
110
|
+
self.notice = "Queue is already empty"
|
|
111
|
+
return False
|
|
112
|
+
if self.delete_armed != self.selected:
|
|
113
|
+
self.delete_armed = self.selected
|
|
114
|
+
self.notice = "Press D again to delete this prompt"
|
|
115
|
+
return False
|
|
116
|
+
|
|
117
|
+
items = self.items
|
|
118
|
+
del items[self.selected]
|
|
119
|
+
self.controller.replace_pending_steer_queued(items)
|
|
120
|
+
self.delete_armed = None
|
|
121
|
+
self.clamp_selection()
|
|
122
|
+
self.notice = "Prompt deleted"
|
|
123
|
+
return True
|
|
124
|
+
|
|
125
|
+
def reorder(self, delta: int) -> bool:
|
|
126
|
+
items = self.items
|
|
127
|
+
destination = self.selected + delta
|
|
128
|
+
if not items or destination < 0 or destination >= len(items):
|
|
129
|
+
return False
|
|
130
|
+
items[self.selected], items[destination] = (
|
|
131
|
+
items[destination],
|
|
132
|
+
items[self.selected],
|
|
133
|
+
)
|
|
134
|
+
self.controller.replace_pending_steer_queued(items)
|
|
135
|
+
self.selected = destination
|
|
136
|
+
self.delete_armed = None
|
|
137
|
+
self.notice = "Prompt moved"
|
|
138
|
+
return True
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
class QueueMenuApp:
|
|
142
|
+
"""Persistent full-screen prompt queue manager."""
|
|
143
|
+
|
|
144
|
+
def __init__(self, controller):
|
|
145
|
+
from prompt_toolkit import Application
|
|
146
|
+
from prompt_toolkit.buffer import Buffer
|
|
147
|
+
from prompt_toolkit.filters import Condition
|
|
148
|
+
from prompt_toolkit.key_binding import KeyBindings
|
|
149
|
+
from prompt_toolkit.layout import HSplit, Layout, VSplit, Window
|
|
150
|
+
from prompt_toolkit.layout.controls import BufferControl, FormattedTextControl
|
|
151
|
+
from prompt_toolkit.layout.dimension import Dimension
|
|
152
|
+
from prompt_toolkit.layout.processors import BeforeInput
|
|
153
|
+
from prompt_toolkit.widgets import Frame
|
|
154
|
+
|
|
155
|
+
self.state = QueueMenuState(controller)
|
|
156
|
+
self._list_control = FormattedTextControl(
|
|
157
|
+
self._render_list, focusable=True, show_cursor=False
|
|
158
|
+
)
|
|
159
|
+
self._editor_buffer = Buffer(multiline=True)
|
|
160
|
+
self._editor_control = BufferControl(
|
|
161
|
+
buffer=self._editor_buffer,
|
|
162
|
+
input_processors=[BeforeInput(self._editor_prefix)],
|
|
163
|
+
focusable=Condition(lambda: self.state.editing),
|
|
164
|
+
)
|
|
165
|
+
self._detail_frame = Frame(
|
|
166
|
+
Window(
|
|
167
|
+
self._editor_control,
|
|
168
|
+
wrap_lines=True,
|
|
169
|
+
always_hide_cursor=Condition(lambda: not self.state.editing),
|
|
170
|
+
),
|
|
171
|
+
title=self._detail_title,
|
|
172
|
+
style="class:tui.body",
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
body = VSplit(
|
|
176
|
+
[
|
|
177
|
+
Frame(
|
|
178
|
+
Window(
|
|
179
|
+
self._list_control,
|
|
180
|
+
wrap_lines=False,
|
|
181
|
+
width=Dimension(weight=2),
|
|
182
|
+
),
|
|
183
|
+
title="Queued prompts",
|
|
184
|
+
style="class:tui.body",
|
|
185
|
+
),
|
|
186
|
+
Window(width=1, char=" "),
|
|
187
|
+
self._detail_frame,
|
|
188
|
+
],
|
|
189
|
+
padding=0,
|
|
190
|
+
)
|
|
191
|
+
root = HSplit(
|
|
192
|
+
[
|
|
193
|
+
Window(
|
|
194
|
+
FormattedTextControl(self._render_header),
|
|
195
|
+
height=2,
|
|
196
|
+
style="class:tui.header",
|
|
197
|
+
),
|
|
198
|
+
body,
|
|
199
|
+
Window(
|
|
200
|
+
FormattedTextControl(self._render_notice),
|
|
201
|
+
height=1,
|
|
202
|
+
style="class:tui.warning",
|
|
203
|
+
),
|
|
204
|
+
Window(
|
|
205
|
+
FormattedTextControl(self._render_footer),
|
|
206
|
+
height=2,
|
|
207
|
+
style="class:tui.help",
|
|
208
|
+
),
|
|
209
|
+
]
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
self._bindings = KeyBindings()
|
|
213
|
+
self._register_bindings()
|
|
214
|
+
self.application = Application(
|
|
215
|
+
layout=Layout(root, focused_element=self._list_control),
|
|
216
|
+
key_bindings=self._bindings,
|
|
217
|
+
full_screen=True,
|
|
218
|
+
mouse_support=True,
|
|
219
|
+
style=on_prompt_toolkit_style(),
|
|
220
|
+
)
|
|
221
|
+
self._sync_editor()
|
|
222
|
+
|
|
223
|
+
def _editor_prefix(self):
|
|
224
|
+
return [("class:tui.label", "EDIT " if self.state.editing else "")]
|
|
225
|
+
|
|
226
|
+
def _detail_title(self) -> str:
|
|
227
|
+
if self.state.adding:
|
|
228
|
+
return "New prompt"
|
|
229
|
+
if self.state.editing:
|
|
230
|
+
return f"Edit prompt {self.state.selected + 1}"
|
|
231
|
+
return "Prompt preview"
|
|
232
|
+
|
|
233
|
+
def _render_header(self):
|
|
234
|
+
count = len(self.state.items)
|
|
235
|
+
return [
|
|
236
|
+
("class:tui.title", " PROMPT QUEUE\n"),
|
|
237
|
+
("class:tui.header", f" {count} item{'s' if count != 1 else ''} waiting"),
|
|
238
|
+
]
|
|
239
|
+
|
|
240
|
+
def _render_list(self):
|
|
241
|
+
items = self.state.items
|
|
242
|
+
if not items:
|
|
243
|
+
return [
|
|
244
|
+
(
|
|
245
|
+
"class:tui.muted",
|
|
246
|
+
"\n Queue is empty.\n\n Press A to add a prompt.",
|
|
247
|
+
)
|
|
248
|
+
]
|
|
249
|
+
fragments = []
|
|
250
|
+
for index, text in enumerate(items):
|
|
251
|
+
style = (
|
|
252
|
+
"class:tui.selected"
|
|
253
|
+
if index == self.state.selected
|
|
254
|
+
else "class:tui.body"
|
|
255
|
+
)
|
|
256
|
+
marker = "▶" if index == self.state.selected else " "
|
|
257
|
+
fragments.extend(
|
|
258
|
+
[
|
|
259
|
+
(style, f" {marker} "),
|
|
260
|
+
(f"{style} class:tui.muted", f"{index + 1:>2} "),
|
|
261
|
+
(style, _preview(text)),
|
|
262
|
+
(style, "\n"),
|
|
263
|
+
]
|
|
264
|
+
)
|
|
265
|
+
return fragments
|
|
266
|
+
|
|
267
|
+
def _render_notice(self):
|
|
268
|
+
return [("class:tui.warning", f" {self.state.notice}")]
|
|
269
|
+
|
|
270
|
+
def _render_footer(self):
|
|
271
|
+
if self.state.editing:
|
|
272
|
+
return [
|
|
273
|
+
("class:tui.help", " "),
|
|
274
|
+
("class:tui.help-key", "Ctrl+S"),
|
|
275
|
+
("class:tui.help", " save "),
|
|
276
|
+
("class:tui.help-key", "Esc"),
|
|
277
|
+
("class:tui.help", " cancel Multi-line editing enabled"),
|
|
278
|
+
]
|
|
279
|
+
return [
|
|
280
|
+
("class:tui.help", " "),
|
|
281
|
+
("class:tui.help-key", "↑↓/JK"),
|
|
282
|
+
("class:tui.help", " select "),
|
|
283
|
+
("class:tui.help-key", "Enter/E"),
|
|
284
|
+
("class:tui.help", " edit "),
|
|
285
|
+
("class:tui.help-key", "A"),
|
|
286
|
+
("class:tui.help", " add "),
|
|
287
|
+
("class:tui.help-key", "D D"),
|
|
288
|
+
("class:tui.help", " delete\n "),
|
|
289
|
+
("class:tui.help-key", "[ ]"),
|
|
290
|
+
("class:tui.help", " reorder "),
|
|
291
|
+
("class:tui.help-key", "Q/Esc"),
|
|
292
|
+
("class:tui.help", " done"),
|
|
293
|
+
]
|
|
294
|
+
|
|
295
|
+
def _sync_editor(self, text: Optional[str] = None) -> None:
|
|
296
|
+
value = self.state.selected_text if text is None else text
|
|
297
|
+
self._editor_buffer.set_document(
|
|
298
|
+
self._editor_buffer.document.__class__(value, cursor_position=len(value)),
|
|
299
|
+
bypass_readonly=True,
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
def _refresh(self) -> None:
|
|
303
|
+
self.application.invalidate()
|
|
304
|
+
|
|
305
|
+
def _focus_list(self) -> None:
|
|
306
|
+
self.application.layout.focus(self._list_control)
|
|
307
|
+
|
|
308
|
+
def _begin_add(self) -> None:
|
|
309
|
+
self.state.begin_add()
|
|
310
|
+
self._sync_editor("")
|
|
311
|
+
self.application.layout.focus(self._editor_control)
|
|
312
|
+
|
|
313
|
+
def _begin_edit(self) -> None:
|
|
314
|
+
if self.state.begin_edit():
|
|
315
|
+
self._sync_editor()
|
|
316
|
+
self.application.layout.focus(self._editor_control)
|
|
317
|
+
|
|
318
|
+
def _cancel_edit(self) -> None:
|
|
319
|
+
self.state.cancel_edit()
|
|
320
|
+
self._sync_editor()
|
|
321
|
+
self._focus_list()
|
|
322
|
+
|
|
323
|
+
def _save_edit(self) -> None:
|
|
324
|
+
if self.state.save(self._editor_buffer.text):
|
|
325
|
+
self._sync_editor()
|
|
326
|
+
self._focus_list()
|
|
327
|
+
|
|
328
|
+
def _move_selection(self, delta: int) -> None:
|
|
329
|
+
self.state.move_selection(delta)
|
|
330
|
+
self._sync_editor()
|
|
331
|
+
|
|
332
|
+
def _register_bindings(self) -> None:
|
|
333
|
+
kb = self._bindings
|
|
334
|
+
|
|
335
|
+
@kb.add("up")
|
|
336
|
+
@kb.add("k")
|
|
337
|
+
def _up(event):
|
|
338
|
+
if not self.state.editing:
|
|
339
|
+
self._move_selection(-1)
|
|
340
|
+
|
|
341
|
+
@kb.add("down")
|
|
342
|
+
@kb.add("j")
|
|
343
|
+
def _down(event):
|
|
344
|
+
if not self.state.editing:
|
|
345
|
+
self._move_selection(1)
|
|
346
|
+
|
|
347
|
+
@kb.add("home")
|
|
348
|
+
def _home(event):
|
|
349
|
+
if not self.state.editing:
|
|
350
|
+
self.state.selected = 0
|
|
351
|
+
self._sync_editor()
|
|
352
|
+
|
|
353
|
+
@kb.add("end")
|
|
354
|
+
def _end(event):
|
|
355
|
+
if not self.state.editing and self.state.items:
|
|
356
|
+
self.state.selected = len(self.state.items) - 1
|
|
357
|
+
self._sync_editor()
|
|
358
|
+
|
|
359
|
+
@kb.add("a")
|
|
360
|
+
def _add(event):
|
|
361
|
+
if not self.state.editing:
|
|
362
|
+
self._begin_add()
|
|
363
|
+
|
|
364
|
+
@kb.add("e")
|
|
365
|
+
@kb.add("enter")
|
|
366
|
+
def _edit(event):
|
|
367
|
+
if not self.state.editing:
|
|
368
|
+
self._begin_edit()
|
|
369
|
+
else:
|
|
370
|
+
self._editor_buffer.insert_text("\n")
|
|
371
|
+
|
|
372
|
+
@kb.add("d")
|
|
373
|
+
def _delete(event):
|
|
374
|
+
if not self.state.editing:
|
|
375
|
+
deleted = self.state.request_delete()
|
|
376
|
+
if deleted:
|
|
377
|
+
self._sync_editor()
|
|
378
|
+
self._refresh()
|
|
379
|
+
|
|
380
|
+
@kb.add("[")
|
|
381
|
+
def _move_up(event):
|
|
382
|
+
if not self.state.editing and self.state.reorder(-1):
|
|
383
|
+
self._sync_editor()
|
|
384
|
+
|
|
385
|
+
@kb.add("]")
|
|
386
|
+
def _move_down(event):
|
|
387
|
+
if not self.state.editing and self.state.reorder(1):
|
|
388
|
+
self._sync_editor()
|
|
389
|
+
|
|
390
|
+
@kb.add("c-s")
|
|
391
|
+
def _save(event):
|
|
392
|
+
if self.state.editing:
|
|
393
|
+
self._save_edit()
|
|
394
|
+
|
|
395
|
+
@kb.add("escape")
|
|
396
|
+
def _escape(event):
|
|
397
|
+
if self.state.editing:
|
|
398
|
+
self._cancel_edit()
|
|
399
|
+
else:
|
|
400
|
+
event.app.exit()
|
|
401
|
+
|
|
402
|
+
@kb.add("q")
|
|
403
|
+
def _quit(event):
|
|
404
|
+
if not self.state.editing:
|
|
405
|
+
event.app.exit()
|
|
406
|
+
|
|
407
|
+
@kb.add("c-c")
|
|
408
|
+
def _ctrl_c(event):
|
|
409
|
+
if self.state.editing:
|
|
410
|
+
self._cancel_edit()
|
|
411
|
+
else:
|
|
412
|
+
event.app.exit()
|
|
413
|
+
|
|
414
|
+
async def run(self) -> None:
|
|
415
|
+
await self.application.run_async()
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
async def run_queue_menu() -> None:
|
|
419
|
+
"""Run the full-screen queue manager."""
|
|
420
|
+
from code_puppy.messaging.pause_controller import get_pause_controller
|
|
421
|
+
|
|
422
|
+
await QueueMenuApp(get_pause_controller()).run()
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
def open_queue_menu_blocking(timeout_s: float = 600.0) -> None:
|
|
426
|
+
"""Run the menu on a worker thread with an isolated event loop."""
|
|
427
|
+
import asyncio
|
|
428
|
+
import concurrent.futures
|
|
429
|
+
|
|
430
|
+
try:
|
|
431
|
+
with concurrent.futures.ThreadPoolExecutor() as executor:
|
|
432
|
+
executor.submit(lambda: asyncio.run(run_queue_menu())).result(
|
|
433
|
+
timeout=timeout_s
|
|
434
|
+
)
|
|
435
|
+
except Exception:
|
|
436
|
+
logger.debug("queue menu failed", exc_info=True)
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
__all__ = [
|
|
440
|
+
"QueueMenuApp",
|
|
441
|
+
"QueueMenuState",
|
|
442
|
+
"open_queue_menu_blocking",
|
|
443
|
+
"run_queue_menu",
|
|
444
|
+
]
|