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,79 @@
|
|
|
1
|
+
"""Register callbacks for the ``stack_dump`` plugin.
|
|
2
|
+
|
|
3
|
+
Wedge forensics: when Code Puppy hangs hard (deadlock, stuck lock
|
|
4
|
+
acquire, wedged event loop), ``kill -USR1 <pid>`` dumps EVERY thread's
|
|
5
|
+
Python stack to ``<state>/logs/stacks.log`` — no debugger, no py-spy,
|
|
6
|
+
no sudo required.
|
|
7
|
+
|
|
8
|
+
Why ``faulthandler`` and not a Python-level ``signal.signal`` handler:
|
|
9
|
+
Python signal handlers only run on the main thread between bytecodes.
|
|
10
|
+
A main thread stuck in a C-level ``lock.acquire()`` never reaches a
|
|
11
|
+
bytecode boundary, so a Python handler would hang right along with it
|
|
12
|
+
— the exact scenario we need to diagnose. ``faulthandler.register``
|
|
13
|
+
installs a C-level handler that writes stacks async-signal-safely,
|
|
14
|
+
even while the main thread is wedged.
|
|
15
|
+
|
|
16
|
+
POSIX-only (``SIGUSR1`` doesn't exist on Windows); silently no-ops
|
|
17
|
+
there and on any registration failure — never crash the app over
|
|
18
|
+
diagnostics plumbing.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import logging
|
|
24
|
+
import os
|
|
25
|
+
|
|
26
|
+
from code_puppy.callbacks import register_callback
|
|
27
|
+
|
|
28
|
+
logger = logging.getLogger(__name__)
|
|
29
|
+
|
|
30
|
+
#: Kept module-global on purpose: faulthandler holds the raw fd, so the
|
|
31
|
+
#: file object must outlive the whole process. Never close it.
|
|
32
|
+
_dump_file = None
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _stacks_log_path() -> str:
|
|
36
|
+
"""Path of the dump log, next to the existing error log."""
|
|
37
|
+
from code_puppy.error_logging import LOGS_DIR
|
|
38
|
+
|
|
39
|
+
return os.path.join(LOGS_DIR, "stacks.log")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _on_startup() -> None:
|
|
43
|
+
"""Arm SIGUSR1 → all-thread stack dump (best-effort, POSIX only)."""
|
|
44
|
+
global _dump_file
|
|
45
|
+
import faulthandler
|
|
46
|
+
import signal
|
|
47
|
+
|
|
48
|
+
if not hasattr(signal, "SIGUSR1") or not hasattr(faulthandler, "register"):
|
|
49
|
+
return # Windows: no SIGUSR1 / no faulthandler.register
|
|
50
|
+
if _dump_file is not None:
|
|
51
|
+
return # already armed (idempotent across repeated startups)
|
|
52
|
+
try:
|
|
53
|
+
from code_puppy.error_logging import _ensure_logs_dir
|
|
54
|
+
|
|
55
|
+
_ensure_logs_dir()
|
|
56
|
+
path = _stacks_log_path()
|
|
57
|
+
_dump_file = open(path, "a", encoding="utf-8")
|
|
58
|
+
# Human breadcrumb: faulthandler output has no pid/timestamp of
|
|
59
|
+
# its own, so mark which process armed the handler and when.
|
|
60
|
+
from datetime import datetime
|
|
61
|
+
|
|
62
|
+
_dump_file.write(
|
|
63
|
+
f"\n=== stack_dump armed: pid={os.getpid()} "
|
|
64
|
+
f"at {datetime.now().isoformat()} "
|
|
65
|
+
f"(kill -USR1 {os.getpid()} to dump all threads) ===\n"
|
|
66
|
+
)
|
|
67
|
+
_dump_file.flush()
|
|
68
|
+
faulthandler.register(signal.SIGUSR1, file=_dump_file, all_threads=True)
|
|
69
|
+
except Exception:
|
|
70
|
+
logger.debug("stack_dump: failed to arm SIGUSR1 handler", exc_info=True)
|
|
71
|
+
try:
|
|
72
|
+
if _dump_file is not None:
|
|
73
|
+
_dump_file.close()
|
|
74
|
+
except Exception:
|
|
75
|
+
pass
|
|
76
|
+
_dump_file = None
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
register_callback("startup", _on_startup)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Status line plugin — a customizable bottom status line for Code Puppy.
|
|
2
|
+
|
|
3
|
+
Mirrors Claude Code's ``statusLine`` feature: you configure a shell command;
|
|
4
|
+
Code Puppy feeds it JSON session data on stdin; whatever the command prints to
|
|
5
|
+
stdout becomes your status line (ANSI colors supported).
|
|
6
|
+
|
|
7
|
+
Configure with the ``/statusline`` command or directly via ``/set``:
|
|
8
|
+
|
|
9
|
+
/set statusline_enabled=true
|
|
10
|
+
/set statusline_command=~/.code_puppy/statusline.sh
|
|
11
|
+
|
|
12
|
+
The command runs throttled in a background thread, so it never blocks the
|
|
13
|
+
prompt. Run ``/statusline json`` to see every field your script receives.
|
|
14
|
+
"""
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""Configuration for the statusline plugin (persisted in puppy.cfg)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
|
|
7
|
+
from code_puppy.config import get_value, set_value
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
KEY_ENABLED = "statusline_enabled"
|
|
12
|
+
KEY_COMMAND = "statusline_command"
|
|
13
|
+
KEY_TIMEOUT = "statusline_timeout_ms"
|
|
14
|
+
KEY_REFRESH = "statusline_refresh_ms"
|
|
15
|
+
KEY_MODE = "statusline_mode"
|
|
16
|
+
|
|
17
|
+
DEFAULT_TIMEOUT_MS = 1000
|
|
18
|
+
DEFAULT_REFRESH_MS = 1000
|
|
19
|
+
DEFAULT_MODE = "replace"
|
|
20
|
+
_VALID_MODES = ("replace", "above", "newline")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _get_int(key: str, default: int) -> int:
|
|
24
|
+
raw = get_value(key)
|
|
25
|
+
if raw is None or raw == "":
|
|
26
|
+
return default
|
|
27
|
+
try:
|
|
28
|
+
return int(str(raw).strip())
|
|
29
|
+
except (TypeError, ValueError):
|
|
30
|
+
return default
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def is_enabled() -> bool:
|
|
34
|
+
raw = get_value(KEY_ENABLED)
|
|
35
|
+
if raw is None or raw == "":
|
|
36
|
+
return False
|
|
37
|
+
return str(raw).strip().lower() in ("1", "true", "yes", "on")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def set_enabled(value: bool) -> None:
|
|
41
|
+
set_value(KEY_ENABLED, "true" if value else "false")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def get_command() -> str:
|
|
45
|
+
return (get_value(KEY_COMMAND) or "").strip()
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def set_command(command: str) -> None:
|
|
49
|
+
set_value(KEY_COMMAND, command or "")
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def get_timeout_ms() -> int:
|
|
53
|
+
return max(100, _get_int(KEY_TIMEOUT, DEFAULT_TIMEOUT_MS))
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def get_refresh_ms() -> int:
|
|
57
|
+
return max(200, _get_int(KEY_REFRESH, DEFAULT_REFRESH_MS))
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def get_mode() -> str:
|
|
61
|
+
raw = (get_value(KEY_MODE) or "").strip().lower()
|
|
62
|
+
return raw if raw in _VALID_MODES else DEFAULT_MODE
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def set_mode(mode: str) -> None:
|
|
66
|
+
mode = (mode or "").strip().lower()
|
|
67
|
+
if mode in _VALID_MODES:
|
|
68
|
+
set_value(KEY_MODE, mode)
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"""Build the JSON session payload fed to the status line command on stdin.
|
|
2
|
+
|
|
3
|
+
Schema mirrors Claude Code's ``statusLine`` stdin contract where the data
|
|
4
|
+
exists in Code Puppy, so scripts written for one are easy to port. Every field
|
|
5
|
+
is best-effort: a failure in one source must never break the payload.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import json
|
|
11
|
+
import logging
|
|
12
|
+
import os
|
|
13
|
+
import subprocess
|
|
14
|
+
from typing import Any, Dict, Optional
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _safe(fn, default=None):
|
|
20
|
+
try:
|
|
21
|
+
return fn()
|
|
22
|
+
except Exception:
|
|
23
|
+
return default
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _model_block() -> Dict[str, Any]:
|
|
27
|
+
from code_puppy.command_line.model_picker_completion import get_active_model
|
|
28
|
+
|
|
29
|
+
model_id = _safe(get_active_model) or "(default)"
|
|
30
|
+
return {"id": model_id, "display_name": model_id}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _agent_block() -> Optional[Dict[str, Any]]:
|
|
34
|
+
from code_puppy.agents.agent_manager import get_current_agent
|
|
35
|
+
|
|
36
|
+
agent = _safe(get_current_agent)
|
|
37
|
+
if not agent:
|
|
38
|
+
return None
|
|
39
|
+
name = getattr(agent, "display_name", None) or getattr(agent, "name", None)
|
|
40
|
+
return {"name": name} if name else None
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def detect_git_branch(cwd: str) -> Optional[str]:
|
|
44
|
+
"""Return the active git branch for cwd, or None outside a branch/repo.
|
|
45
|
+
|
|
46
|
+
Windows hardening: ``capture_output=True`` uses reader threads, and if the
|
|
47
|
+
spawned git (or a grandchild) keeps a pipe write-handle open, ``run`` hangs
|
|
48
|
+
forever joining them -- even with a timeout -- which deadlocks the ACP event
|
|
49
|
+
loop when this runs from a ``post_tool_call`` hook. Route stdout through a
|
|
50
|
+
temp file (no reader threads) and detach stdin from any inherited pipe.
|
|
51
|
+
"""
|
|
52
|
+
try:
|
|
53
|
+
import tempfile
|
|
54
|
+
|
|
55
|
+
with tempfile.TemporaryFile() as out_f:
|
|
56
|
+
proc = subprocess.run(
|
|
57
|
+
["git", "rev-parse", "--abbrev-ref", "HEAD"],
|
|
58
|
+
cwd=cwd,
|
|
59
|
+
stdin=subprocess.DEVNULL,
|
|
60
|
+
stdout=out_f,
|
|
61
|
+
stderr=subprocess.DEVNULL,
|
|
62
|
+
timeout=5,
|
|
63
|
+
encoding="utf-8",
|
|
64
|
+
errors="replace",
|
|
65
|
+
)
|
|
66
|
+
if proc.returncode == 0:
|
|
67
|
+
out_f.seek(0)
|
|
68
|
+
branch = out_f.read().decode("utf-8", "replace").strip()
|
|
69
|
+
return branch or None
|
|
70
|
+
except Exception:
|
|
71
|
+
return None
|
|
72
|
+
return None
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
# Back-compat alias: quick-resume (config.py) reuses this public helper.
|
|
76
|
+
_git_branch = detect_git_branch
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _context_block() -> Dict[str, Any]:
|
|
80
|
+
block: Dict[str, Any] = {}
|
|
81
|
+
try:
|
|
82
|
+
from code_puppy.token_usage import get_current_usage
|
|
83
|
+
|
|
84
|
+
usage = get_current_usage()
|
|
85
|
+
if usage is not None:
|
|
86
|
+
block["total_input_tokens"] = int(getattr(usage, "used_tokens", 0))
|
|
87
|
+
block["context_window_size"] = int(getattr(usage, "capacity", 0))
|
|
88
|
+
block["used_percentage"] = round(float(usage.percent), 1)
|
|
89
|
+
block["remaining_percentage"] = round(100.0 - float(usage.percent), 1)
|
|
90
|
+
block["indicator"] = usage.indicator
|
|
91
|
+
except Exception:
|
|
92
|
+
pass
|
|
93
|
+
return block
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _tokens_per_second() -> float:
|
|
97
|
+
try:
|
|
98
|
+
from code_puppy.status_display import StatusDisplay
|
|
99
|
+
|
|
100
|
+
return round(float(StatusDisplay.get_current_rate()), 1)
|
|
101
|
+
except Exception:
|
|
102
|
+
return 0.0
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def build_payload() -> Dict[str, Any]:
|
|
106
|
+
"""Assemble the full session payload (all fields best-effort)."""
|
|
107
|
+
from code_puppy.config import get_puppy_name
|
|
108
|
+
|
|
109
|
+
cwd = _safe(os.getcwd, "") or ""
|
|
110
|
+
payload: Dict[str, Any] = {
|
|
111
|
+
"cwd": cwd,
|
|
112
|
+
"puppy_name": _safe(get_puppy_name) or "code-puppy",
|
|
113
|
+
"model": _model_block(),
|
|
114
|
+
"workspace": {"current_dir": cwd},
|
|
115
|
+
"context_window": _context_block(),
|
|
116
|
+
"tokens_per_second": _tokens_per_second(),
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
agent = _agent_block()
|
|
120
|
+
if agent:
|
|
121
|
+
payload["agent"] = agent
|
|
122
|
+
|
|
123
|
+
branch = detect_git_branch(cwd)
|
|
124
|
+
if branch:
|
|
125
|
+
payload["workspace"]["git_branch"] = branch
|
|
126
|
+
|
|
127
|
+
try:
|
|
128
|
+
from importlib.metadata import version
|
|
129
|
+
|
|
130
|
+
payload["version"] = version("code-puppy")
|
|
131
|
+
except Exception:
|
|
132
|
+
pass
|
|
133
|
+
|
|
134
|
+
return payload
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def build_payload_json() -> str:
|
|
138
|
+
return json.dumps(build_payload(), indent=2)
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""Monkey-patch ``get_prompt_with_active_model`` to render the status line.
|
|
2
|
+
|
|
3
|
+
Same proven seam the ``context_indicator`` plugin uses. The status command's
|
|
4
|
+
stdout may contain ANSI color codes, so we convert it to prompt_toolkit
|
|
5
|
+
``FormattedText`` via ``ANSI``.
|
|
6
|
+
|
|
7
|
+
Three modes (config ``statusline_mode``):
|
|
8
|
+
|
|
9
|
+
* ``replace`` (default): the custom status line **replaces** Code Puppy's
|
|
10
|
+
default prompt content (puppy/agent/model/cwd), keeping only the trailing
|
|
11
|
+
arrow. No duplicated info, no extra stacked line.
|
|
12
|
+
* ``above``: the custom status line is shown on its own line *above* the
|
|
13
|
+
unchanged default prompt.
|
|
14
|
+
* ``newline``: like ``replace`` but the trailing arrow is pushed to its own
|
|
15
|
+
line, so the user types below the status line::
|
|
16
|
+
|
|
17
|
+
[model] code-puppy (main) 0.9%ctx
|
|
18
|
+
>>> typed text
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import logging
|
|
24
|
+
|
|
25
|
+
from .config import get_mode
|
|
26
|
+
from .runner import get_status_text
|
|
27
|
+
|
|
28
|
+
logger = logging.getLogger(__name__)
|
|
29
|
+
|
|
30
|
+
_PATCH_ATTR = "_statusline_original_prompt_fn"
|
|
31
|
+
_DEFAULT_ARROW = ">>> "
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _render(formatted_text, base: str):
|
|
35
|
+
from prompt_toolkit.formatted_text import ANSI, FormattedText, to_formatted_text
|
|
36
|
+
|
|
37
|
+
text = get_status_text()
|
|
38
|
+
if not text:
|
|
39
|
+
return formatted_text
|
|
40
|
+
|
|
41
|
+
try:
|
|
42
|
+
status_fragments = list(to_formatted_text(ANSI(text)))
|
|
43
|
+
except Exception:
|
|
44
|
+
logger.debug("statusline: failed to parse status text", exc_info=True)
|
|
45
|
+
return formatted_text
|
|
46
|
+
|
|
47
|
+
mode = get_mode()
|
|
48
|
+
|
|
49
|
+
if mode == "above":
|
|
50
|
+
# Status line on its own line, default prompt unchanged below it.
|
|
51
|
+
return FormattedText(status_fragments + [("", "\n")] + list(formatted_text))
|
|
52
|
+
|
|
53
|
+
if mode == "newline":
|
|
54
|
+
# Status line on its own line, arrow on the next line.
|
|
55
|
+
arrow = base if base else _DEFAULT_ARROW
|
|
56
|
+
return FormattedText(status_fragments + [("", "\n"), ("class:arrow", arrow)])
|
|
57
|
+
|
|
58
|
+
# replace mode (default): status line + trailing arrow on same line.
|
|
59
|
+
arrow = base if base else _DEFAULT_ARROW
|
|
60
|
+
return FormattedText(status_fragments + [("class:arrow", " " + arrow)])
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def install_prompt_patch() -> None:
|
|
64
|
+
"""Wrap ``get_prompt_with_active_model`` exactly once."""
|
|
65
|
+
from code_puppy.command_line import prompt_toolkit_completion as ptc
|
|
66
|
+
|
|
67
|
+
if getattr(ptc, _PATCH_ATTR, None) is not None:
|
|
68
|
+
return # already patched
|
|
69
|
+
|
|
70
|
+
original = ptc.get_prompt_with_active_model
|
|
71
|
+
setattr(ptc, _PATCH_ATTR, original)
|
|
72
|
+
|
|
73
|
+
def patched(base: str = ">>> "):
|
|
74
|
+
return _render(original(base), base)
|
|
75
|
+
|
|
76
|
+
ptc.get_prompt_with_active_model = patched
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""Wire up the statusline plugin."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from typing import Any, List, Optional, Tuple
|
|
7
|
+
|
|
8
|
+
from code_puppy.callbacks import register_callback
|
|
9
|
+
|
|
10
|
+
from .payload import detect_git_branch
|
|
11
|
+
from .prompt_patch import install_prompt_patch
|
|
12
|
+
from .statusline_command import handle_statusline_command, statusline_command_help
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _on_startup() -> None:
|
|
18
|
+
try:
|
|
19
|
+
install_prompt_patch()
|
|
20
|
+
except Exception:
|
|
21
|
+
logger.debug("statusline: failed to install prompt patch", exc_info=True)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _custom_command_help() -> List[Tuple[str, str]]:
|
|
25
|
+
return statusline_command_help()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _handle_custom_command(command: str, name: str) -> Optional[Any]:
|
|
29
|
+
try:
|
|
30
|
+
return handle_statusline_command(command, name)
|
|
31
|
+
except Exception:
|
|
32
|
+
logger.debug("statusline: command handler failed", exc_info=True)
|
|
33
|
+
return None
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
register_callback("git_branch_provider", detect_git_branch)
|
|
37
|
+
register_callback("startup", _on_startup)
|
|
38
|
+
register_callback("custom_command_help", _custom_command_help)
|
|
39
|
+
register_callback("custom_command", _handle_custom_command)
|
|
40
|
+
|
|
41
|
+
# Install immediately too, in case startup already fired before this plugin
|
|
42
|
+
# loaded (plugin load order is not guaranteed relative to the startup hook).
|
|
43
|
+
_on_startup()
|
|
44
|
+
|
|
45
|
+
logger.debug("statusline plugin registered")
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"""Run the status line command (JSON on stdin -> stdout) without blocking.
|
|
2
|
+
|
|
3
|
+
The prompt is re-rendered frequently; running a shell command synchronously
|
|
4
|
+
each time would make the prompt janky. So we cache the last stdout and refresh
|
|
5
|
+
it in a background thread no more often than ``statusline_refresh_ms``. The
|
|
6
|
+
prompt always reads the cached value instantly.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import logging
|
|
12
|
+
import subprocess
|
|
13
|
+
import threading
|
|
14
|
+
import time
|
|
15
|
+
|
|
16
|
+
from .config import get_command, get_refresh_ms, get_timeout_ms, is_enabled
|
|
17
|
+
from .payload import build_payload_json
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
_lock = threading.Lock()
|
|
22
|
+
_cached_output: str = ""
|
|
23
|
+
_last_run_monotonic: float = 0.0
|
|
24
|
+
_running: bool = False
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _run_command_blocking(command: str) -> str:
|
|
28
|
+
payload = build_payload_json()
|
|
29
|
+
try:
|
|
30
|
+
proc = subprocess.run(
|
|
31
|
+
command,
|
|
32
|
+
shell=True,
|
|
33
|
+
input=payload,
|
|
34
|
+
capture_output=True,
|
|
35
|
+
text=True,
|
|
36
|
+
encoding="utf-8", # explicit UTF-8: prevents cp1252 crash on Windows with umlauts
|
|
37
|
+
errors="replace", # never raise UnicodeDecodeError — bad chars become '?'
|
|
38
|
+
timeout=get_timeout_ms() / 1000.0,
|
|
39
|
+
)
|
|
40
|
+
except subprocess.TimeoutExpired:
|
|
41
|
+
return ""
|
|
42
|
+
except Exception:
|
|
43
|
+
logger.debug("statusline command failed", exc_info=True)
|
|
44
|
+
return ""
|
|
45
|
+
out = (proc.stdout or "").strip("\n")
|
|
46
|
+
# Status line is a single line: collapse any extra newlines.
|
|
47
|
+
return out.replace("\n", " ").strip()
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _refresh_async(command: str) -> None:
|
|
51
|
+
global _cached_output, _running, _last_run_monotonic
|
|
52
|
+
try:
|
|
53
|
+
result = _run_command_blocking(command)
|
|
54
|
+
with _lock:
|
|
55
|
+
_cached_output = result
|
|
56
|
+
_last_run_monotonic = time.monotonic()
|
|
57
|
+
finally:
|
|
58
|
+
with _lock:
|
|
59
|
+
_running = False
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def get_status_text() -> str:
|
|
63
|
+
"""Return the status line, refreshing in the background when stale.
|
|
64
|
+
|
|
65
|
+
First call (cold cache) runs **synchronously** once so the very first
|
|
66
|
+
prompt — including the one shown at startup — has a status line. After that
|
|
67
|
+
it never blocks: it returns the cached value and schedules a background
|
|
68
|
+
refresh when the value goes stale.
|
|
69
|
+
"""
|
|
70
|
+
global _running, _last_run_monotonic, _cached_output
|
|
71
|
+
if not is_enabled():
|
|
72
|
+
return ""
|
|
73
|
+
command = get_command()
|
|
74
|
+
if not command:
|
|
75
|
+
return ""
|
|
76
|
+
|
|
77
|
+
now = time.monotonic()
|
|
78
|
+
with _lock:
|
|
79
|
+
cached = _cached_output
|
|
80
|
+
cold = _last_run_monotonic == 0.0
|
|
81
|
+
if cold:
|
|
82
|
+
# Reserve the slot so we don't also spawn an async refresh below.
|
|
83
|
+
_last_run_monotonic = now
|
|
84
|
+
|
|
85
|
+
if cold:
|
|
86
|
+
# One-time synchronous warm-up so the FIRST prompt (startup) has a
|
|
87
|
+
# status line. Bounded by the command timeout.
|
|
88
|
+
result = _run_command_blocking(command)
|
|
89
|
+
with _lock:
|
|
90
|
+
_cached_output = result
|
|
91
|
+
_last_run_monotonic = time.monotonic()
|
|
92
|
+
return result
|
|
93
|
+
|
|
94
|
+
with _lock:
|
|
95
|
+
due = (now - _last_run_monotonic) * 1000.0 >= get_refresh_ms()
|
|
96
|
+
should_start = due and not _running
|
|
97
|
+
if should_start:
|
|
98
|
+
_running = True
|
|
99
|
+
# Reserve the slot immediately so concurrent prompt renders don't
|
|
100
|
+
# spawn a thundering herd of refreshes.
|
|
101
|
+
_last_run_monotonic = now
|
|
102
|
+
|
|
103
|
+
if should_start:
|
|
104
|
+
threading.Thread(target=_refresh_async, args=(command,), daemon=True).start()
|
|
105
|
+
|
|
106
|
+
return cached
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def run_once_sync() -> str:
|
|
110
|
+
"""Run the command synchronously once (for /statusline show preview)."""
|
|
111
|
+
command = get_command()
|
|
112
|
+
if not command:
|
|
113
|
+
return ""
|
|
114
|
+
return _run_command_blocking(command)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def reset_cache() -> None:
|
|
118
|
+
global _cached_output, _last_run_monotonic, _running
|
|
119
|
+
with _lock:
|
|
120
|
+
_cached_output = ""
|
|
121
|
+
_last_run_monotonic = 0.0
|
|
122
|
+
_running = False
|