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.
Files changed (313) hide show
  1. code_puppy_core_plugins/__init__.py +5 -0
  2. code_puppy_core_plugins/acp/README.md +266 -0
  3. code_puppy_core_plugins/acp/__init__.py +8 -0
  4. code_puppy_core_plugins/acp/agent.py +494 -0
  5. code_puppy_core_plugins/acp/bridge.py +309 -0
  6. code_puppy_core_plugins/acp/capabilities.py +73 -0
  7. code_puppy_core_plugins/acp/commands.py +116 -0
  8. code_puppy_core_plugins/acp/content.py +115 -0
  9. code_puppy_core_plugins/acp/io_delegation.py +231 -0
  10. code_puppy_core_plugins/acp/mcp_config.py +66 -0
  11. code_puppy_core_plugins/acp/permissions.py +159 -0
  12. code_puppy_core_plugins/acp/persistence.py +197 -0
  13. code_puppy_core_plugins/acp/register_callbacks.py +110 -0
  14. code_puppy_core_plugins/acp/replay.py +114 -0
  15. code_puppy_core_plugins/acp/session.py +329 -0
  16. code_puppy_core_plugins/acp/session_config.py +179 -0
  17. code_puppy_core_plugins/acp/state.py +135 -0
  18. code_puppy_core_plugins/agent_skills/__init__.py +22 -0
  19. code_puppy_core_plugins/agent_skills/config.py +208 -0
  20. code_puppy_core_plugins/agent_skills/discovery.py +337 -0
  21. code_puppy_core_plugins/agent_skills/downloader.py +392 -0
  22. code_puppy_core_plugins/agent_skills/enabled_skills.py +82 -0
  23. code_puppy_core_plugins/agent_skills/installer.py +22 -0
  24. code_puppy_core_plugins/agent_skills/metadata.py +219 -0
  25. code_puppy_core_plugins/agent_skills/prompt_builder.py +49 -0
  26. code_puppy_core_plugins/agent_skills/provider.py +52 -0
  27. code_puppy_core_plugins/agent_skills/register_callbacks.py +309 -0
  28. code_puppy_core_plugins/agent_skills/remote_catalog.py +322 -0
  29. code_puppy_core_plugins/agent_skills/skill_catalog.py +257 -0
  30. code_puppy_core_plugins/agent_skills/skill_commands.py +102 -0
  31. code_puppy_core_plugins/agent_skills/skills_install_menu.py +698 -0
  32. code_puppy_core_plugins/agent_skills/skills_menu.py +794 -0
  33. code_puppy_core_plugins/aws_bedrock/__init__.py +14 -0
  34. code_puppy_core_plugins/aws_bedrock/config.py +101 -0
  35. code_puppy_core_plugins/aws_bedrock/register_callbacks.py +243 -0
  36. code_puppy_core_plugins/aws_bedrock/utils.py +186 -0
  37. code_puppy_core_plugins/azure_foundry/README.md +238 -0
  38. code_puppy_core_plugins/azure_foundry/__init__.py +15 -0
  39. code_puppy_core_plugins/azure_foundry/config.py +126 -0
  40. code_puppy_core_plugins/azure_foundry/discovery.py +189 -0
  41. code_puppy_core_plugins/azure_foundry/register_callbacks.py +506 -0
  42. code_puppy_core_plugins/azure_foundry/token.py +182 -0
  43. code_puppy_core_plugins/azure_foundry/utils.py +375 -0
  44. code_puppy_core_plugins/btw/__init__.py +1 -0
  45. code_puppy_core_plugins/btw/inline_view.py +135 -0
  46. code_puppy_core_plugins/btw/register_callbacks.py +106 -0
  47. code_puppy_core_plugins/btw/side_query.py +91 -0
  48. code_puppy_core_plugins/chatgpt_oauth/IMAGEGEN_SKILL.md +83 -0
  49. code_puppy_core_plugins/chatgpt_oauth/__init__.py +8 -0
  50. code_puppy_core_plugins/chatgpt_oauth/config.py +52 -0
  51. code_puppy_core_plugins/chatgpt_oauth/image_generation.py +253 -0
  52. code_puppy_core_plugins/chatgpt_oauth/image_tool.py +70 -0
  53. code_puppy_core_plugins/chatgpt_oauth/oauth_flow.py +461 -0
  54. code_puppy_core_plugins/chatgpt_oauth/register_callbacks.py +288 -0
  55. code_puppy_core_plugins/chatgpt_oauth/test_plugin.py +663 -0
  56. code_puppy_core_plugins/chatgpt_oauth/usage.py +113 -0
  57. code_puppy_core_plugins/chatgpt_oauth/utils.py +534 -0
  58. code_puppy_core_plugins/claude_code_hooks/__init__.py +1 -0
  59. code_puppy_core_plugins/claude_code_hooks/config.py +137 -0
  60. code_puppy_core_plugins/claude_code_hooks/register_callbacks.py +383 -0
  61. code_puppy_core_plugins/claude_code_oauth/README.md +167 -0
  62. code_puppy_core_plugins/claude_code_oauth/SETUP.md +93 -0
  63. code_puppy_core_plugins/claude_code_oauth/__init__.py +25 -0
  64. code_puppy_core_plugins/claude_code_oauth/config.py +59 -0
  65. code_puppy_core_plugins/claude_code_oauth/fast_mode.py +122 -0
  66. code_puppy_core_plugins/claude_code_oauth/prompt_handler.py +64 -0
  67. code_puppy_core_plugins/claude_code_oauth/register_callbacks.py +719 -0
  68. code_puppy_core_plugins/claude_code_oauth/test_plugin.py +285 -0
  69. code_puppy_core_plugins/claude_code_oauth/token_refresh_heartbeat.py +240 -0
  70. code_puppy_core_plugins/claude_code_oauth/utils.py +663 -0
  71. code_puppy_core_plugins/code_puppy_agent/AGENTS_AND_TOOLS.md +155 -0
  72. code_puppy_core_plugins/code_puppy_agent/MODELS_AND_MCP.md +104 -0
  73. code_puppy_core_plugins/code_puppy_agent/PLUGINS_AND_CALLBACKS.md +147 -0
  74. code_puppy_core_plugins/code_puppy_agent/SESSIONS_AND_HISTORY.md +39 -0
  75. code_puppy_core_plugins/code_puppy_agent/SKILL.md +180 -0
  76. code_puppy_core_plugins/code_puppy_agent/SKILLS_SYSTEM.md +136 -0
  77. code_puppy_core_plugins/code_puppy_agent/SYSTEM_PROMPT_CONFIG_AND_I18N.md +160 -0
  78. code_puppy_core_plugins/code_puppy_agent/__init__.py +5 -0
  79. code_puppy_core_plugins/code_puppy_agent/register_callbacks.py +46 -0
  80. code_puppy_core_plugins/computer_use/NativeCapture.swift +123 -0
  81. code_puppy_core_plugins/computer_use/README.md +109 -0
  82. code_puppy_core_plugins/computer_use/__init__.py +1 -0
  83. code_puppy_core_plugins/computer_use/accessibility.py +163 -0
  84. code_puppy_core_plugins/computer_use/activation.py +75 -0
  85. code_puppy_core_plugins/computer_use/backend.py +584 -0
  86. code_puppy_core_plugins/computer_use/backend_types.py +5 -0
  87. code_puppy_core_plugins/computer_use/capture.py +110 -0
  88. code_puppy_core_plugins/computer_use/commands.py +62 -0
  89. code_puppy_core_plugins/computer_use/geometry.py +81 -0
  90. code_puppy_core_plugins/computer_use/inline_image.py +56 -0
  91. code_puppy_core_plugins/computer_use/keycodes.py +47 -0
  92. code_puppy_core_plugins/computer_use/policy.py +144 -0
  93. code_puppy_core_plugins/computer_use/register_callbacks.py +96 -0
  94. code_puppy_core_plugins/computer_use/safety.py +18 -0
  95. code_puppy_core_plugins/computer_use/settle.py +64 -0
  96. code_puppy_core_plugins/computer_use/state.py +86 -0
  97. code_puppy_core_plugins/computer_use/tools.py +469 -0
  98. code_puppy_core_plugins/context_indicator/__init__.py +14 -0
  99. code_puppy_core_plugins/context_indicator/register_callbacks.py +249 -0
  100. code_puppy_core_plugins/context_indicator/usage.py +40 -0
  101. code_puppy_core_plugins/copilot_auth/__init__.py +11 -0
  102. code_puppy_core_plugins/copilot_auth/config.py +93 -0
  103. code_puppy_core_plugins/copilot_auth/reasoning_client.py +409 -0
  104. code_puppy_core_plugins/copilot_auth/register_callbacks.py +460 -0
  105. code_puppy_core_plugins/copilot_auth/utils.py +587 -0
  106. code_puppy_core_plugins/customizable_commands/__init__.py +0 -0
  107. code_puppy_core_plugins/customizable_commands/register_callbacks.py +464 -0
  108. code_puppy_core_plugins/dbos_durable_exec/__init__.py +1 -0
  109. code_puppy_core_plugins/dbos_durable_exec/cancel.py +15 -0
  110. code_puppy_core_plugins/dbos_durable_exec/commands.py +31 -0
  111. code_puppy_core_plugins/dbos_durable_exec/config.py +31 -0
  112. code_puppy_core_plugins/dbos_durable_exec/lifecycle.py +83 -0
  113. code_puppy_core_plugins/dbos_durable_exec/register_callbacks.py +45 -0
  114. code_puppy_core_plugins/dbos_durable_exec/runtime.py +65 -0
  115. code_puppy_core_plugins/dbos_durable_exec/startup_lock.py +155 -0
  116. code_puppy_core_plugins/dbos_durable_exec/workflow_ids.py +16 -0
  117. code_puppy_core_plugins/dbos_durable_exec/wrapper.py +58 -0
  118. code_puppy_core_plugins/destructive_command_guard/AGENTS.md +79 -0
  119. code_puppy_core_plugins/destructive_command_guard/__init__.py +14 -0
  120. code_puppy_core_plugins/destructive_command_guard/detector.py +374 -0
  121. code_puppy_core_plugins/destructive_command_guard/register_callbacks.py +165 -0
  122. code_puppy_core_plugins/emoji_filter/__init__.py +12 -0
  123. code_puppy_core_plugins/emoji_filter/config.py +26 -0
  124. code_puppy_core_plugins/emoji_filter/register_callbacks.py +238 -0
  125. code_puppy_core_plugins/emoji_filter/stripper.py +45 -0
  126. code_puppy_core_plugins/empty_html_comment_filter/register_callbacks.py +116 -0
  127. code_puppy_core_plugins/example_custom_command/README.md +328 -0
  128. code_puppy_core_plugins/example_custom_command/register_callbacks.py +66 -0
  129. code_puppy_core_plugins/file_permission_handler/__init__.py +4 -0
  130. code_puppy_core_plugins/file_permission_handler/register_callbacks.py +575 -0
  131. code_puppy_core_plugins/flux_bootstrap/__init__.py +5 -0
  132. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/_docs/README.md +173 -0
  133. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/_docs/cheatsheet.md +86 -0
  134. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/_docs/pipeline.md +167 -0
  135. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/_docs/synopsis.md +392 -0
  136. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/about.md +51 -0
  137. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/address-feedback.md +129 -0
  138. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/ask.md +211 -0
  139. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/aug.md +181 -0
  140. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/auto-pilot.md +71 -0
  141. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/cheatsheet.md +16 -0
  142. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/commit.md +103 -0
  143. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/config.md +108 -0
  144. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/create-pr.md +195 -0
  145. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/exec.md +221 -0
  146. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/new.md +151 -0
  147. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/qa.md +208 -0
  148. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/rebase.md +180 -0
  149. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/review.md +304 -0
  150. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/split.md +120 -0
  151. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/squash-commits.md +179 -0
  152. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/status.md +46 -0
  153. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/tests.md +155 -0
  154. code_puppy_core_plugins/flux_bootstrap/bundled/scripts/flux_about.py +154 -0
  155. code_puppy_core_plugins/flux_bootstrap/bundled/scripts/flux_cheatsheet.py +248 -0
  156. code_puppy_core_plugins/flux_bootstrap/bundled/scripts/flux_status.py +345 -0
  157. code_puppy_core_plugins/flux_bootstrap/installer.py +256 -0
  158. code_puppy_core_plugins/flux_bootstrap/register_callbacks.py +71 -0
  159. code_puppy_core_plugins/force_push_guard/__init__.py +5 -0
  160. code_puppy_core_plugins/force_push_guard/detector.py +96 -0
  161. code_puppy_core_plugins/force_push_guard/register_callbacks.py +161 -0
  162. code_puppy_core_plugins/fork/__init__.py +7 -0
  163. code_puppy_core_plugins/fork/register_callbacks.py +445 -0
  164. code_puppy_core_plugins/frontend_emitter/__init__.py +25 -0
  165. code_puppy_core_plugins/frontend_emitter/emitter.py +249 -0
  166. code_puppy_core_plugins/frontend_emitter/register_callbacks.py +412 -0
  167. code_puppy_core_plugins/frontend_emitter/session_context.py +50 -0
  168. code_puppy_core_plugins/grok_oauth/__init__.py +6 -0
  169. code_puppy_core_plugins/grok_oauth/config.py +49 -0
  170. code_puppy_core_plugins/grok_oauth/oauth_flow.py +213 -0
  171. code_puppy_core_plugins/grok_oauth/register_callbacks.py +132 -0
  172. code_puppy_core_plugins/grok_oauth/utils.py +195 -0
  173. code_puppy_core_plugins/herdr/README.md +168 -0
  174. code_puppy_core_plugins/herdr/__init__.py +9 -0
  175. code_puppy_core_plugins/herdr/client.py +339 -0
  176. code_puppy_core_plugins/herdr/register_callbacks.py +140 -0
  177. code_puppy_core_plugins/herdr/reporter.py +212 -0
  178. code_puppy_core_plugins/herdr/smoke.py +143 -0
  179. code_puppy_core_plugins/herdr/sources.py +133 -0
  180. code_puppy_core_plugins/hook_creator/__init__.py +1 -0
  181. code_puppy_core_plugins/hook_creator/register_callbacks.py +33 -0
  182. code_puppy_core_plugins/hook_manager/__init__.py +1 -0
  183. code_puppy_core_plugins/hook_manager/config.py +290 -0
  184. code_puppy_core_plugins/hook_manager/hooks_menu.py +571 -0
  185. code_puppy_core_plugins/hook_manager/register_callbacks.py +227 -0
  186. code_puppy_core_plugins/meta_oauth/__init__.py +1 -0
  187. code_puppy_core_plugins/meta_oauth/config.py +55 -0
  188. code_puppy_core_plugins/meta_oauth/oauth_flow.py +109 -0
  189. code_puppy_core_plugins/meta_oauth/register_callbacks.py +154 -0
  190. code_puppy_core_plugins/meta_oauth/utils.py +252 -0
  191. code_puppy_core_plugins/namespace_skill_search/README.md +118 -0
  192. code_puppy_core_plugins/namespace_skill_search/__init__.py +15 -0
  193. code_puppy_core_plugins/namespace_skill_search/namespaces.py +125 -0
  194. code_puppy_core_plugins/namespace_skill_search/register_callbacks.py +99 -0
  195. code_puppy_core_plugins/namespace_skill_search/search_tool.py +158 -0
  196. code_puppy_core_plugins/no_tools/__init__.py +0 -0
  197. code_puppy_core_plugins/no_tools/register_callbacks.py +42 -0
  198. code_puppy_core_plugins/oauth_pasteback.py +110 -0
  199. code_puppy_core_plugins/oauth_puppy_html.py +194 -0
  200. code_puppy_core_plugins/obsidian_agent/README.md +52 -0
  201. code_puppy_core_plugins/obsidian_agent/__init__.py +8 -0
  202. code_puppy_core_plugins/obsidian_agent/agent_obsidian.py +147 -0
  203. code_puppy_core_plugins/obsidian_agent/register_callbacks.py +13 -0
  204. code_puppy_core_plugins/ollama/__init__.py +1 -0
  205. code_puppy_core_plugins/ollama/register_callbacks.py +127 -0
  206. code_puppy_core_plugins/ollama_setup/__init__.py +5 -0
  207. code_puppy_core_plugins/ollama_setup/completer.py +38 -0
  208. code_puppy_core_plugins/ollama_setup/register_callbacks.py +419 -0
  209. code_puppy_core_plugins/plugin_list/__init__.py +0 -0
  210. code_puppy_core_plugins/plugin_list/plugin_contributions.py +348 -0
  211. code_puppy_core_plugins/plugin_list/plugin_meta.py +67 -0
  212. code_puppy_core_plugins/plugin_list/plugin_text_utils.py +173 -0
  213. code_puppy_core_plugins/plugin_list/plugins_menu.py +455 -0
  214. code_puppy_core_plugins/plugin_list/plugins_menu_layout.py +194 -0
  215. code_puppy_core_plugins/plugin_list/plugins_menu_render.py +431 -0
  216. code_puppy_core_plugins/plugin_list/project_trust_flow.py +162 -0
  217. code_puppy_core_plugins/plugin_list/register_callbacks.py +281 -0
  218. code_puppy_core_plugins/pop_command/__init__.py +1 -0
  219. code_puppy_core_plugins/pop_command/register_callbacks.py +191 -0
  220. code_puppy_core_plugins/prompt_newline/__init__.py +13 -0
  221. code_puppy_core_plugins/prompt_newline/config.py +21 -0
  222. code_puppy_core_plugins/prompt_newline/register_callbacks.py +163 -0
  223. code_puppy_core_plugins/prune/__init__.py +1 -0
  224. code_puppy_core_plugins/prune/prune_menu.py +308 -0
  225. code_puppy_core_plugins/prune/prune_model.py +517 -0
  226. code_puppy_core_plugins/prune/prune_render.py +354 -0
  227. code_puppy_core_plugins/prune/register_callbacks.py +388 -0
  228. code_puppy_core_plugins/puppy_kennel/README.md +203 -0
  229. code_puppy_core_plugins/puppy_kennel/__init__.py +6 -0
  230. code_puppy_core_plugins/puppy_kennel/commands.py +206 -0
  231. code_puppy_core_plugins/puppy_kennel/config.py +36 -0
  232. code_puppy_core_plugins/puppy_kennel/kennel.py +368 -0
  233. code_puppy_core_plugins/puppy_kennel/packer.py +152 -0
  234. code_puppy_core_plugins/puppy_kennel/recorder.py +83 -0
  235. code_puppy_core_plugins/puppy_kennel/register_callbacks.py +109 -0
  236. code_puppy_core_plugins/puppy_kennel/retriever.py +28 -0
  237. code_puppy_core_plugins/puppy_kennel/schema.py +78 -0
  238. code_puppy_core_plugins/puppy_kennel/state.py +68 -0
  239. code_puppy_core_plugins/puppy_kennel/tools.py +449 -0
  240. code_puppy_core_plugins/puppy_kennel/wings.py +110 -0
  241. code_puppy_core_plugins/puppy_spinner/README.md +93 -0
  242. code_puppy_core_plugins/puppy_spinner/__init__.py +11 -0
  243. code_puppy_core_plugins/puppy_spinner/builtin_frames.py +172 -0
  244. code_puppy_core_plugins/puppy_spinner/commands.py +116 -0
  245. code_puppy_core_plugins/puppy_spinner/picker.py +379 -0
  246. code_puppy_core_plugins/puppy_spinner/register_callbacks.py +193 -0
  247. code_puppy_core_plugins/puppy_spinner/spinners.py +444 -0
  248. code_puppy_core_plugins/quick_resume/__init__.py +0 -0
  249. code_puppy_core_plugins/quick_resume/register_callbacks.py +77 -0
  250. code_puppy_core_plugins/review_pr/__init__.py +1 -0
  251. code_puppy_core_plugins/review_pr/register_callbacks.py +162 -0
  252. code_puppy_core_plugins/shell_safety/__init__.py +6 -0
  253. code_puppy_core_plugins/shell_safety/agent_shell_safety.py +69 -0
  254. code_puppy_core_plugins/shell_safety/command_cache.py +156 -0
  255. code_puppy_core_plugins/shell_safety/register_callbacks.py +203 -0
  256. code_puppy_core_plugins/stack_dump/__init__.py +1 -0
  257. code_puppy_core_plugins/stack_dump/register_callbacks.py +79 -0
  258. code_puppy_core_plugins/statusline/__init__.py +14 -0
  259. code_puppy_core_plugins/statusline/config.py +68 -0
  260. code_puppy_core_plugins/statusline/payload.py +138 -0
  261. code_puppy_core_plugins/statusline/prompt_patch.py +76 -0
  262. code_puppy_core_plugins/statusline/register_callbacks.py +45 -0
  263. code_puppy_core_plugins/statusline/runner.py +122 -0
  264. code_puppy_core_plugins/statusline/statusline_command.py +197 -0
  265. code_puppy_core_plugins/steer_queue/__init__.py +10 -0
  266. code_puppy_core_plugins/steer_queue/queue_menu.py +444 -0
  267. code_puppy_core_plugins/steer_queue/register_callbacks.py +138 -0
  268. code_puppy_core_plugins/subagent_panel/README.md +102 -0
  269. code_puppy_core_plugins/subagent_panel/__init__.py +24 -0
  270. code_puppy_core_plugins/subagent_panel/coalesce_patch.py +242 -0
  271. code_puppy_core_plugins/subagent_panel/panel_render.py +145 -0
  272. code_puppy_core_plugins/subagent_panel/register_callbacks.py +535 -0
  273. code_puppy_core_plugins/subagent_panel/resume_repaint.py +112 -0
  274. code_puppy_core_plugins/subagent_panel/state.py +231 -0
  275. code_puppy_core_plugins/switch_agent_resume/__init__.py +5 -0
  276. code_puppy_core_plugins/switch_agent_resume/register_callbacks.py +314 -0
  277. code_puppy_core_plugins/theme/README.md +86 -0
  278. code_puppy_core_plugins/theme/__init__.py +14 -0
  279. code_puppy_core_plugins/theme/bundled_palettes.py +346 -0
  280. code_puppy_core_plugins/theme/content_styles.py +146 -0
  281. code_puppy_core_plugins/theme/osc_palette.py +178 -0
  282. code_puppy_core_plugins/theme/picker.py +358 -0
  283. code_puppy_core_plugins/theme/prompt_toolkit_theme.py +113 -0
  284. code_puppy_core_plugins/theme/register_callbacks.py +348 -0
  285. code_puppy_core_plugins/theme/rich_themes.py +202 -0
  286. code_puppy_core_plugins/theme/themes.py +720 -0
  287. code_puppy_core_plugins/timestamp_heartbeat/__init__.py +1 -0
  288. code_puppy_core_plugins/timestamp_heartbeat/register_callbacks.py +114 -0
  289. code_puppy_core_plugins/token_ratio_learner/__init__.py +5 -0
  290. code_puppy_core_plugins/token_ratio_learner/ratios.py +208 -0
  291. code_puppy_core_plugins/token_ratio_learner/register_callbacks.py +213 -0
  292. code_puppy_core_plugins/universal_constructor/__init__.py +13 -0
  293. code_puppy_core_plugins/universal_constructor/models.py +138 -0
  294. code_puppy_core_plugins/universal_constructor/provider.py +62 -0
  295. code_puppy_core_plugins/universal_constructor/register_callbacks.py +75 -0
  296. code_puppy_core_plugins/universal_constructor/registry.py +302 -0
  297. code_puppy_core_plugins/universal_constructor/sandbox.py +584 -0
  298. code_puppy_core_plugins/wide_completion_menu/__init__.py +6 -0
  299. code_puppy_core_plugins/wide_completion_menu/register_callbacks.py +165 -0
  300. code_puppy_core_plugins/wiggum/__init__.py +1 -0
  301. code_puppy_core_plugins/wiggum/goal_runs.py +91 -0
  302. code_puppy_core_plugins/wiggum/judge.py +299 -0
  303. code_puppy_core_plugins/wiggum/judge_config.py +249 -0
  304. code_puppy_core_plugins/wiggum/judges_menu.py +814 -0
  305. code_puppy_core_plugins/wiggum/register_callbacks.py +569 -0
  306. code_puppy_core_plugins/wiggum/state.py +89 -0
  307. code_puppy_core_plugins/yolo_cli/__init__.py +1 -0
  308. code_puppy_core_plugins/yolo_cli/register_callbacks.py +41 -0
  309. code_puppy_core_plugins-0.0.2.dist-info/METADATA +64 -0
  310. code_puppy_core_plugins-0.0.2.dist-info/RECORD +313 -0
  311. code_puppy_core_plugins-0.0.2.dist-info/WHEEL +4 -0
  312. code_puppy_core_plugins-0.0.2.dist-info/entry_points.txt +55 -0
  313. 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
+ ]