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,455 @@
1
+ """Interactive TUI for managing plugins.
2
+
3
+ Launch with ``/plugins`` to browse and toggle plugins on/off.
4
+ Built with prompt_toolkit, following the same pattern as the skills menu.
5
+
6
+ This module is the *controller*: terminal sizing, key bindings, app lifecycle,
7
+ and plugin state mutation. All rendering (fragment construction, padding,
8
+ emoji stripping) lives in :mod:`plugins_menu_render` so each module has one
9
+ reason to change.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import shutil
15
+ import sys
16
+ import time
17
+ from typing import List, Optional, Tuple
18
+
19
+ from prompt_toolkit.application import Application
20
+ from prompt_toolkit.application.current import get_app
21
+ from prompt_toolkit.filters import Condition
22
+ from prompt_toolkit.layout import Window
23
+ from prompt_toolkit.layout.controls import FormattedTextControl
24
+ from prompt_toolkit.widgets import TextArea
25
+
26
+ from code_puppy_core_plugins.plugin_list.plugins_menu_layout import (
27
+ build_key_bindings,
28
+ build_layout,
29
+ )
30
+
31
+ from code_puppy.command_line.pagination import (
32
+ ensure_visible_page,
33
+ get_total_pages,
34
+ )
35
+ from code_puppy_core_plugins.plugin_list.plugin_text_utils import (
36
+ Fragments,
37
+ count_lines,
38
+ drop_leading_lines,
39
+ )
40
+ from code_puppy_core_plugins.plugin_list.plugins_menu_render import (
41
+ fill_pane,
42
+ render_detail,
43
+ render_list,
44
+ )
45
+ from code_puppy.tools.command_runner import set_awaiting_user_input
46
+ from code_puppy.callbacks import on_prompt_toolkit_style
47
+
48
+ PAGE_SIZE = 20
49
+
50
+
51
+ class _PluginEntry:
52
+ """Lightweight struct for a plugin row.
53
+
54
+ ``status`` is "loaded" for imported plugins; project plugins held back
55
+ by the trust gate carry their gate status instead ("untrusted",
56
+ "changed", "disabled", "error") so the TUI can show them without
57
+ pretending they're active.
58
+ """
59
+
60
+ __slots__ = ("name", "tier", "status")
61
+
62
+ def __init__(self, name: str, tier: str, status: str = "loaded") -> None:
63
+ self.name = name
64
+ self.tier = tier
65
+ self.status = status
66
+
67
+
68
+ class PluginsMenu:
69
+ """Interactive TUI for enabling/disabling plugins.
70
+
71
+ The view (``plugins_menu_render``) reads the following attributes on this
72
+ object — keep them stable to avoid breaking the render contract:
73
+
74
+ * ``plugins``, ``disabled``, ``selected_idx``, ``current_page``, ``page_size``
75
+ * ``project_dir`` (posix string or None)
76
+ * ``trust_target``, ``trust_error``, ``trust_feedback`` (popup state)
77
+ * ``_detail_cols``, ``_pane_rows``
78
+ * ``_changed``
79
+ * ``_current()``
80
+
81
+ Entries carry a ``status`` ("loaded" or a trust-gate status) — the
82
+ renderer branches on it, so keep it stable too.
83
+ """
84
+
85
+ def __init__(self, focus_plugin: Optional[str] = None) -> None:
86
+ """*focus_plugin* preselects that plugin; if it's trust-gated, the
87
+ risk-acceptance popup opens immediately (used by ``/plugins enable``
88
+ to bring the user straight to the ceremony)."""
89
+ self.plugins: List[_PluginEntry] = []
90
+ self.disabled: set[str] = set()
91
+ self.project_dir: Optional[str] = None
92
+ self.lock_builtin: bool = False
93
+ self.hidden_builtin_count: int = 0
94
+
95
+ # The trust modal disables list bindings while open, keeping accept-word
96
+ # typing from triggering shortcuts; focus remains on its input.
97
+ self.trust_target: Optional[_PluginEntry] = None
98
+ self.trust_error: str = ""
99
+ self.trust_feedback: str = ""
100
+ self.trust_input: TextArea = TextArea(
101
+ multiline=False, height=1, accept_handler=self._accept_trust
102
+ )
103
+ self._modal_open = Condition(lambda: self.trust_target is not None)
104
+
105
+ self.selected_idx = 0
106
+ self.current_page = 0
107
+ # Mirrors PAGE_SIZE so the renderer's pagination math and the
108
+ # keybindings (which use the module constant) can't drift apart.
109
+ self.page_size = PAGE_SIZE
110
+ self.result: Optional[str] = None
111
+ self._changed = False
112
+
113
+ self.detail_scroll = 0
114
+
115
+ # Track pane height so cell-diff redraws overwrite stale glyphs below
116
+ # short content.
117
+ self._menu_cols = 30
118
+ self._detail_cols = 60
119
+ self._pane_rows = 20
120
+ self._last_size: Tuple[int, int] = (0, 0)
121
+
122
+ self.menu_control: Optional[FormattedTextControl] = None
123
+ self.detail_control: Optional[FormattedTextControl] = None
124
+ self.detail_window: Optional[Window] = None
125
+
126
+ self._refresh_data()
127
+
128
+ if focus_plugin:
129
+ for i, entry in enumerate(self.plugins):
130
+ if entry.name == focus_plugin:
131
+ self.selected_idx = i
132
+ self.current_page = ensure_visible_page(
133
+ i, self.current_page, len(self.plugins), PAGE_SIZE
134
+ )
135
+ if entry.status in ("untrusted", "changed"):
136
+ self._open_trust_modal(entry)
137
+ break
138
+
139
+ # -- data helpers ------------------------------------------------------
140
+
141
+ def _refresh_data(self) -> None:
142
+ from code_puppy.plugins import (
143
+ get_loaded_plugins,
144
+ get_project_plugin_status,
145
+ get_project_plugins_directory,
146
+ )
147
+ from code_puppy.plugins.config import get_disabled_plugins
148
+ from code_puppy.plugins.config import (
149
+ get_lock_builtin_plugins,
150
+ )
151
+
152
+ loaded = get_loaded_plugins()
153
+ self.disabled = get_disabled_plugins()
154
+ self.lock_builtin = get_lock_builtin_plugins()
155
+
156
+ project_dir = get_project_plugins_directory()
157
+ self.project_dir = project_dir.as_posix() if project_dir else None
158
+
159
+ entries: List[_PluginEntry] = []
160
+ self.hidden_builtin_count = 0
161
+ for tier in ("builtin", "user", "project"):
162
+ for name in sorted(loaded.get(tier, [])):
163
+ # When locked, builtins are managed/protected — hide them so
164
+ # they can't be toggled (the config layer refuses anyway).
165
+ if self.lock_builtin and tier == "builtin":
166
+ self.hidden_builtin_count += 1
167
+ continue
168
+ entries.append(_PluginEntry(name, tier))
169
+
170
+ # Project plugins held back by the trust gate — shown so Enter can
171
+ # open the ceremony popup on them.
172
+ statuses = get_project_plugin_status()
173
+ shown = {e.name for e in entries if e.tier == "project"}
174
+ for name in sorted(statuses):
175
+ if statuses[name] != "loaded" and name not in shown:
176
+ entries.append(_PluginEntry(name, "project", statuses[name]))
177
+
178
+ self.plugins = entries
179
+
180
+ # Keep selection in range if the list shrank.
181
+ if self.selected_idx >= len(self.plugins):
182
+ self.selected_idx = max(0, len(self.plugins) - 1)
183
+
184
+ def _current(self) -> Optional[_PluginEntry]:
185
+ if 0 <= self.selected_idx < len(self.plugins):
186
+ return self.plugins[self.selected_idx]
187
+ return None
188
+
189
+ def _toggle_current(self) -> None:
190
+ entry = self._current()
191
+ if not entry:
192
+ return
193
+ self.trust_feedback = ""
194
+
195
+ if entry.status in ("untrusted", "changed"):
196
+ # Ceremony required — open the risk-acceptance popup.
197
+ self._open_trust_modal(entry)
198
+ return
199
+ if entry.status in ("disabled", "error"):
200
+ # Already trusted; just (re)activate — no ceremony needed.
201
+ from code_puppy_core_plugins.plugin_list.project_trust_flow import (
202
+ activate_project_plugin,
203
+ )
204
+
205
+ _ok, message = activate_project_plugin(entry.name)
206
+ self.trust_feedback = message
207
+ self.detail_scroll = 0
208
+ self._refresh_data()
209
+ self.update_display()
210
+ return
211
+
212
+ from code_puppy.plugins.config import set_plugin_disabled
213
+
214
+ is_disabled = entry.name in self.disabled
215
+ changed = set_plugin_disabled(entry.name, not is_disabled)
216
+ if changed:
217
+ self._changed = True
218
+ self.detail_scroll = 0
219
+ self._refresh_data()
220
+ self.update_display()
221
+
222
+ # -- trust popup ---------------------------------------------------------
223
+
224
+ def _open_trust_modal(self, entry: _PluginEntry) -> None:
225
+ self.trust_target = entry
226
+ self.trust_error = ""
227
+ self.trust_input.text = ""
228
+ try:
229
+ get_app().layout.focus(self.trust_input)
230
+ except Exception:
231
+ pass # no running app (tests) — state alone drives the logic
232
+ self.update_display()
233
+
234
+ def _close_trust_modal(self) -> None:
235
+ self.trust_target = None
236
+ self.trust_error = ""
237
+ self.trust_input.text = ""
238
+ try:
239
+ get_app().layout.focus(self.menu_control)
240
+ except Exception:
241
+ pass
242
+ self.update_display()
243
+
244
+ def _accept_trust(self, buff) -> bool:
245
+ """Accept-handler for the popup input. Returns False to clear the box."""
246
+ entry = self.trust_target
247
+ if entry is None:
248
+ return False
249
+ from code_puppy_core_plugins.plugin_list.project_trust_flow import (
250
+ ACCEPT_WORD,
251
+ grant_trust_and_load,
252
+ )
253
+
254
+ if buff.text.strip().lower() != ACCEPT_WORD:
255
+ self.trust_error = (
256
+ f"That isn't '{ACCEPT_WORD}' — type it exactly, or press Esc to cancel."
257
+ )
258
+ self.update_display()
259
+ return False
260
+
261
+ _ok, message = grant_trust_and_load(entry.name)
262
+ self.trust_feedback = message
263
+ self._refresh_data()
264
+ self._close_trust_modal()
265
+ return False
266
+
267
+ # -- render compatibility ---------------------------------------------
268
+
269
+ def _render_list(self) -> Fragments:
270
+ """Return rendered list fragments for existing tests/callers."""
271
+ return render_list(self)
272
+
273
+ def _render_detail(self) -> Fragments:
274
+ """Return rendered detail fragments for existing tests/callers."""
275
+ return render_detail(self)
276
+
277
+ # -- display update ----------------------------------------------------
278
+
279
+ def update_display(self) -> None:
280
+ # fill_pane writes every cell of every row — see its docstring for
281
+ # the stale-glyph rationale.
282
+ if self.menu_control:
283
+ self.menu_control.text = fill_pane(
284
+ render_list(self), self._menu_cols, self._pane_rows
285
+ )
286
+ if self.detail_control:
287
+ sliced = drop_leading_lines(render_detail(self), self.detail_scroll)
288
+ self.detail_control.text = fill_pane(
289
+ sliced, self._detail_cols, self._pane_rows
290
+ )
291
+
292
+ def _max_detail_scroll(self) -> int:
293
+ """Topmost line we may scroll to, keeping a screenful visible."""
294
+ total = count_lines(render_detail(self))
295
+ visible = 1
296
+ if self.detail_window is not None and self.detail_window.render_info:
297
+ visible = max(1, self.detail_window.render_info.window_height)
298
+ return max(0, total - visible)
299
+
300
+ def _scroll_detail(self, delta: int) -> None:
301
+ new = max(0, min(self.detail_scroll + delta, self._max_detail_scroll()))
302
+ if new != self.detail_scroll:
303
+ self.detail_scroll = new
304
+ self.update_display()
305
+
306
+ # -- application -------------------------------------------------------
307
+
308
+ def _measure_terminal(self) -> Tuple[int, int]:
309
+ """Return (cols, rows) of the current terminal, with sane fallbacks."""
310
+ try:
311
+ size = shutil.get_terminal_size(fallback=(120, 40))
312
+ return max(60, size.columns), max(15, size.lines)
313
+ except Exception:
314
+ return 120, 40
315
+
316
+ def _recompute_dimensions(self) -> bool:
317
+ """Re-measure the terminal and recompute pane widths.
318
+
319
+ Returns True when the size actually changed. The width-callable
320
+ closures in ``run`` read ``self._menu_cols`` / ``self._detail_cols``
321
+ on every render, so updating these here automatically reflows the
322
+ layout on terminal resize.
323
+ """
324
+ cols, rows = self._measure_terminal()
325
+ if self._last_size == (cols, rows):
326
+ return False
327
+ self._last_size = (cols, rows)
328
+ # Two side-by-side Frames cost 4 columns of border (1 per side, per
329
+ # frame). Anything more leaves dead space on the right edge.
330
+ usable_cols = max(40, cols - 4)
331
+ # 35% / 65% split, with a minimum so the menu pane is always usable.
332
+ self._menu_cols = max(20, min(40, int(usable_cols * 0.35)))
333
+ self._detail_cols = max(20, usable_cols - self._menu_cols)
334
+ # Reserve 2 rows for the Frame's top + bottom borders.
335
+ self._pane_rows = max(5, rows - 2)
336
+ return True
337
+
338
+ def _set_selection(self, new_idx: int) -> None:
339
+ """Move selection to *new_idx* (clamped), resetting detail scroll.
340
+
341
+ Single chokepoint for every selection mutation -- ``_move_selection``,
342
+ the jump-to-first/last actions, and the page jumps all funnel through
343
+ here so the "reset detail scroll + keep selection's page visible"
344
+ contract can't drift between callers.
345
+ """
346
+ if not self.plugins:
347
+ return
348
+ new_idx = max(0, min(new_idx, len(self.plugins) - 1))
349
+ if new_idx == self.selected_idx:
350
+ return
351
+ self.selected_idx = new_idx
352
+ self.detail_scroll = 0
353
+ self.current_page = ensure_visible_page(
354
+ self.selected_idx,
355
+ self.current_page,
356
+ len(self.plugins),
357
+ PAGE_SIZE,
358
+ )
359
+
360
+ def _move_selection(self, delta: int) -> None:
361
+ """Shift the selection by *delta*, clamped, and keep the page in view."""
362
+ self._set_selection(self.selected_idx + delta)
363
+
364
+ def _change_page(self, delta: int) -> None:
365
+ """Move the page by *delta* (clamped) and jump selection to its head."""
366
+ total_pages = get_total_pages(len(self.plugins), PAGE_SIZE)
367
+ new_page = max(0, min(self.current_page + delta, total_pages - 1))
368
+ if new_page == self.current_page:
369
+ return
370
+ self.current_page = new_page
371
+ self._set_selection(self.current_page * PAGE_SIZE)
372
+
373
+ def run(self) -> Optional[str]:
374
+ self.menu_control = FormattedTextControl(text="", focusable=True)
375
+ self.detail_control = FormattedTextControl(text="")
376
+
377
+ self._recompute_dimensions()
378
+
379
+ layout = build_layout(self)
380
+ if self.trust_target is not None:
381
+ # Popup pre-opened (focus_plugin ceremony): focus its input now
382
+ # that the layout exists — _open_trust_modal ran before any app.
383
+ try:
384
+ layout.focus(self.trust_input)
385
+ except Exception:
386
+ pass
387
+ kb = build_key_bindings(self)
388
+
389
+ app = Application(
390
+ layout=layout,
391
+ key_bindings=kb,
392
+ full_screen=False,
393
+ mouse_support=False,
394
+ style=on_prompt_toolkit_style(),
395
+ )
396
+
397
+ # Recompute fixed-width wrapping before each render so SIGWINCH updates
398
+ # dimensions and content; the helper is a no-op when size is unchanged.
399
+ def _on_before_render(_app: Application) -> None:
400
+ if self._recompute_dimensions():
401
+ self.update_display()
402
+
403
+ app.before_render += _on_before_render
404
+
405
+ set_awaiting_user_input(True)
406
+
407
+ sys.stdout.write("\033[?1049h") # Enter alternate buffer
408
+ sys.stdout.write("\033[2J\033[H") # Clear and home
409
+ sys.stdout.flush()
410
+ time.sleep(0.05)
411
+
412
+ try:
413
+ self.update_display()
414
+
415
+ sys.stdout.write("\033[2J\033[H")
416
+ sys.stdout.flush()
417
+
418
+ app.run(in_thread=True)
419
+
420
+ finally:
421
+ sys.stdout.write("\033[?1049l") # Exit alternate buffer
422
+ sys.stdout.flush()
423
+
424
+ try:
425
+ import termios
426
+
427
+ termios.tcflush(sys.stdin.fileno(), termios.TCIFLUSH)
428
+ except Exception:
429
+ pass
430
+
431
+ time.sleep(0.1)
432
+ set_awaiting_user_input(False)
433
+
434
+ return self.result
435
+
436
+
437
+ def run_plugins_menu(focus_plugin: Optional[str] = None) -> Optional[str]:
438
+ """Entry point: create and run the plugins TUI, return the result.
439
+
440
+ *focus_plugin* preselects a plugin and, when it's trust-gated, opens
441
+ the risk-acceptance popup straight away.
442
+ """
443
+ from code_puppy.messaging import emit_warning
444
+
445
+ menu = PluginsMenu(focus_plugin=focus_plugin)
446
+ result = menu.run()
447
+
448
+ if menu._changed:
449
+ emit_warning("Restart Code Puppy for plugin changes to take effect.")
450
+
451
+ return result
452
+
453
+
454
+ # Re-export for callers that don't want to know about the render split.
455
+ __all__ = ["PluginsMenu", "Fragments", "run_plugins_menu"]
@@ -0,0 +1,194 @@
1
+ """Layout + keybinding construction for the /plugins TUI.
2
+
3
+ Split from ``plugins_menu`` the same way ``plugins_menu_render`` was: these
4
+ are pure *construction* helpers that read the menu's state and hand back
5
+ prompt_toolkit objects. No app state lives here.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from typing import TYPE_CHECKING
11
+
12
+ from prompt_toolkit.key_binding import KeyBindings
13
+ from prompt_toolkit.layout import (
14
+ ConditionalContainer,
15
+ Dimension,
16
+ Float,
17
+ FloatContainer,
18
+ HSplit,
19
+ Layout,
20
+ VSplit,
21
+ Window,
22
+ )
23
+ from prompt_toolkit.layout.controls import FormattedTextControl
24
+ from prompt_toolkit.widgets import Frame
25
+
26
+ if TYPE_CHECKING: # pragma: no cover - import-cycle guard
27
+ from code_puppy_core_plugins.plugin_list.plugins_menu import PluginsMenu
28
+
29
+
30
+ def build_key_bindings(menu: "PluginsMenu") -> KeyBindings:
31
+ """Wire keys to match the ``inspect_history`` plugin's mental model.
32
+
33
+ Both plugins share the same split-pane shape (list + scrollable
34
+ detail). Keeping the bindings aligned means muscle memory carries
35
+ over. The mental model (lifted from inspect_history):
36
+
37
+ * Left-hand keys (``h``, ``j``) move things UP.
38
+ * Right-hand keys (``k``, ``l``) move things DOWN.
39
+ * ``h``/``l`` (and ``left``/``right``) scroll the *detail* pane.
40
+ * ``j``/``k`` (and ``up``/``down``, ``c-p``/``c-n``) move the
41
+ *selection* in the list.
42
+ * ``pageup``/``pagedown`` page through the list.
43
+ * ``g``/``home`` and ``G``/``end`` jump to first/last.
44
+ """
45
+ kb = KeyBindings()
46
+
47
+ # Disable list bindings while the trust popup is open so accept-word typing
48
+ # cannot trigger shortcuts.
49
+ no_modal = ~menu._modal_open
50
+
51
+ # -- Selection (j = up, k = down -- inspect_history convention) ---
52
+ @kb.add("up", filter=no_modal)
53
+ @kb.add("c-p", filter=no_modal)
54
+ @kb.add("j", filter=no_modal)
55
+ def _(event):
56
+ menu._move_selection(-1)
57
+ menu.update_display()
58
+
59
+ @kb.add("down", filter=no_modal)
60
+ @kb.add("c-n", filter=no_modal)
61
+ @kb.add("k", filter=no_modal)
62
+ def _(event):
63
+ menu._move_selection(+1)
64
+ menu.update_display()
65
+
66
+ # -- Page through the list -----------------------------------------
67
+ @kb.add("pageup", filter=no_modal)
68
+ def _(event):
69
+ menu._change_page(-1)
70
+ menu.update_display()
71
+
72
+ @kb.add("pagedown", filter=no_modal)
73
+ def _(event):
74
+ menu._change_page(+1)
75
+ menu.update_display()
76
+
77
+ # -- Jump to first / last ------------------------------------------
78
+ @kb.add("home", filter=no_modal)
79
+ @kb.add("g", filter=no_modal)
80
+ def _(event):
81
+ menu._set_selection(0)
82
+ menu.update_display()
83
+
84
+ @kb.add("end", filter=no_modal)
85
+ @kb.add("G", filter=no_modal)
86
+ def _(event):
87
+ menu._set_selection(len(menu.plugins) - 1)
88
+ menu.update_display()
89
+
90
+ # -- Detail pane scroll (h/left = up, l/right = down) --------------
91
+ @kb.add("h", filter=no_modal)
92
+ @kb.add("left", filter=no_modal)
93
+ def _(event):
94
+ menu._scroll_detail(-1)
95
+
96
+ @kb.add("l", filter=no_modal)
97
+ @kb.add("right", filter=no_modal)
98
+ def _(event):
99
+ menu._scroll_detail(+1)
100
+
101
+ # -- Actions / exit ------------------------------------------------
102
+ @kb.add("enter", filter=no_modal)
103
+ def _(event):
104
+ menu._toggle_current()
105
+ menu.result = "changed"
106
+
107
+ @kb.add("q", filter=no_modal)
108
+ @kb.add("escape", filter=no_modal)
109
+ @kb.add("c-c", filter=no_modal)
110
+ def _(event):
111
+ menu.result = "quit"
112
+ event.app.exit()
113
+
114
+ # -- Trust popup: Esc / Ctrl+C cancel (Enter is handled by the
115
+ # focused TextArea's accept_handler) ----------------------------
116
+ @kb.add("escape", filter=menu._modal_open)
117
+ @kb.add("c-c", filter=menu._modal_open)
118
+ def _(event):
119
+ menu._close_trust_modal()
120
+
121
+ return kb
122
+
123
+
124
+ def build_layout(menu: "PluginsMenu") -> Layout:
125
+ """Build the side-by-side Plugins / Details layout plus the trust float.
126
+
127
+ Pane widths are *callables* so they track the live terminal: when the
128
+ window is resized, ``_recompute_dimensions`` updates the cached cols
129
+ and these closures hand prompt_toolkit the fresh numbers on the very
130
+ next render. ``wrap_lines=False`` is critical: auto-wrap bleeds
131
+ characters into the divider/border column and leaves stale glyphs on
132
+ redraw. Long lines (description, path) are pre-wrapped by the renderer.
133
+ """
134
+
135
+ def menu_width() -> Dimension:
136
+ return Dimension(min=20, max=menu._menu_cols, preferred=menu._menu_cols)
137
+
138
+ def detail_width() -> Dimension:
139
+ return Dimension(min=20, max=menu._detail_cols, preferred=menu._detail_cols)
140
+
141
+ def pane_height() -> Dimension:
142
+ return Dimension(min=5, max=menu._pane_rows, preferred=menu._pane_rows)
143
+
144
+ menu_window = Window(
145
+ content=menu.menu_control,
146
+ wrap_lines=False,
147
+ width=menu_width,
148
+ height=pane_height,
149
+ )
150
+ detail_window = Window(
151
+ content=menu.detail_control,
152
+ wrap_lines=False,
153
+ width=detail_width,
154
+ height=pane_height,
155
+ )
156
+ menu.detail_window = detail_window
157
+
158
+ menu_frame = Frame(menu_window, title="Plugins")
159
+ detail_frame = Frame(detail_window, title="Details")
160
+
161
+ # Render the centered popup only during the ceremony; its focused TextArea
162
+ # owns printable keys while list shortcuts stay disabled.
163
+ from code_puppy_core_plugins.plugin_list.plugins_menu_render import (
164
+ render_trust_modal,
165
+ )
166
+
167
+ def modal_width() -> Dimension:
168
+ cols = min(64, max(40, menu._menu_cols + menu._detail_cols - 8))
169
+ return Dimension(min=40, max=cols, preferred=cols)
170
+
171
+ modal_body = HSplit(
172
+ [
173
+ Window(
174
+ content=FormattedTextControl(lambda: render_trust_modal(menu)),
175
+ wrap_lines=False,
176
+ dont_extend_height=True,
177
+ ),
178
+ menu.trust_input,
179
+ ],
180
+ width=modal_width,
181
+ )
182
+ modal_float = Float(
183
+ content=ConditionalContainer(
184
+ Frame(modal_body, title="Enable project plugin?"),
185
+ filter=menu._modal_open,
186
+ )
187
+ )
188
+
189
+ return Layout(
190
+ FloatContainer(
191
+ content=VSplit([menu_frame, detail_frame]),
192
+ floats=[modal_float],
193
+ )
194
+ )