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,231 @@
1
+ """Thread-safe registry of active sub-agents + status derivation.
2
+
3
+ The puppy spinner repaints from a background thread ~20x/second, so all reads
4
+ here must be cheap and lock-guarded.
5
+
6
+ Registration is driven by the sub-agent INVOCATION banner (which carries the
7
+ exact name, session_type, model AND session_id), so attribution is exact even
8
+ for parallel sub-agents -- no FIFO guessing, no session-id parsing. Status
9
+ updates (``record_event``) are UPDATE-ONLY: an event for an unregistered
10
+ session is ignored, which cleanly filters out the MAIN agent's own stream
11
+ events (the main agent is never registered).
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import os
17
+ import threading
18
+ import time
19
+ from typing import Any, Dict, List, Optional
20
+
21
+ # session_id -> {session_id, parent, name, model, status, start, last_seen}
22
+ _AGENTS: "Dict[str, Dict[str, Any]]" = {}
23
+ _LOCK = threading.RLock()
24
+
25
+ # --- tunables (env-overridable) -------------------------------------------
26
+ IDLE_PRUNE_S = float(os.environ.get("SUBAGENT_PANEL_IDLE_S", "600"))
27
+
28
+ # Single-char braille spinner frames (defined via escapes to keep the source
29
+ # emoji-free; braille isn't emoji but escapes dodge any filter ambiguity).
30
+ SPINNER_FRAMES = [
31
+ "\u280b",
32
+ "\u2819",
33
+ "\u2839",
34
+ "\u2838",
35
+ "\u283c",
36
+ "\u2834",
37
+ "\u2826",
38
+ "\u2827",
39
+ "\u2807",
40
+ "\u280f",
41
+ ]
42
+
43
+
44
+ # Registration / teardown (invocation + response banners)
45
+ def register(
46
+ session_id: Optional[str],
47
+ name: str,
48
+ model: Optional[str] = None,
49
+ parent: Optional[str] = None,
50
+ ) -> None:
51
+ if not session_id:
52
+ return
53
+ now = time.time()
54
+ with _LOCK:
55
+ # Re-invocation of an existing session: keep its original start time.
56
+ existing = _AGENTS.get(session_id)
57
+ _AGENTS[session_id] = {
58
+ "session_id": session_id,
59
+ "parent": parent,
60
+ "name": name,
61
+ "model": model,
62
+ "status": "starting",
63
+ "start": existing["start"] if existing else now,
64
+ "last_seen": now,
65
+ }
66
+
67
+
68
+ def finish(session_id: Optional[str]) -> None:
69
+ if not session_id:
70
+ return
71
+ with _LOCK:
72
+ _AGENTS.pop(session_id, None)
73
+
74
+
75
+ def mark_done(session_id: Optional[str]) -> None:
76
+ """Mark a sub-agent completed but KEEP it in the live tree (frozen) until its
77
+ root flushes. This avoids the vanish-then-reappear gap that happens if a
78
+ nested child is popped the instant it finishes while its parent runs on."""
79
+ if not session_id:
80
+ return
81
+ now = time.time()
82
+ with _LOCK:
83
+ entry = _AGENTS.get(session_id)
84
+ if entry is None:
85
+ return
86
+ entry["done"] = True
87
+ entry["status"] = "completed"
88
+ entry["end"] = now
89
+ entry["last_seen"] = now
90
+
91
+
92
+ def mark_failed(session_id: Optional[str]) -> None:
93
+ """Mark a sub-agent FAILED (errored after exhausting retries) but KEEP it in
94
+ the live tree until its root flushes -- same lifecycle as mark_done, but the
95
+ renderer paints it red 'failed' with an X instead of green 'completed'."""
96
+ if not session_id:
97
+ return
98
+ now = time.time()
99
+ with _LOCK:
100
+ entry = _AGENTS.get(session_id)
101
+ if entry is None:
102
+ return
103
+ entry["done"] = True
104
+ entry["failed"] = True
105
+ entry["status"] = "failed"
106
+ entry["end"] = now
107
+ entry["last_seen"] = now
108
+
109
+
110
+ def clear() -> None:
111
+ """Drop ALL tracked sub-agents. Called when the top-level turn ends so a
112
+ root that errored/was cancelled (and thus never flushed) can't leak its
113
+ 'completed' children into the next prompt's live block."""
114
+ with _LOCK:
115
+ _AGENTS.clear()
116
+
117
+
118
+ # Live status (stream_event updates only)
119
+ def record_event(session_id: Optional[str], event_type: str, event_data: Any) -> None:
120
+ if not session_id:
121
+ return
122
+ status = _derive_status(event_type, event_data)
123
+ now = time.time()
124
+ with _LOCK:
125
+ entry = _AGENTS.get(session_id)
126
+ if entry is None:
127
+ return # not a registered sub-agent (e.g. the main agent) -> ignore
128
+ entry["last_seen"] = now
129
+ if status:
130
+ entry["status"] = status
131
+
132
+
133
+ # Reads
134
+ def snapshot() -> List[Dict[str, Any]]:
135
+ """Return active sub-agents (oldest first), pruning idle/stale ones.
136
+
137
+ DONE entries are never pruned by idle -- they stay in the live tree (shown
138
+ as 'completed') until their root flushes them, so a finished child never
139
+ vanishes mid-run.
140
+
141
+ PARENTS are never idle-pruned either: a parent that has invoked children and
142
+ is merely AWAITING them emits no stream events of its own, so its last_seen
143
+ goes stale -- but it is busy, not idle. Pruning it would orphan its whole
144
+ subtree, making the children re-render as depth-0 roots.
145
+
146
+ ROOT rows (parent=None) are never idle-pruned either. A root is the visible
147
+ anchor of a live user-initiated ``invoke_agent`` call; silently deleting it
148
+ while its work is still in flight (e.g. a slow tool call that emits no
149
+ stream events for many minutes) would strand any completed sibling in state
150
+ with no flush trigger, and hide from the user that a real invocation is
151
+ still running. Roots exit via one of three legitimate paths only:
152
+ completion (``_handle_frozen`` -> ``mark_done`` -> ``_maybe_flush_group``),
153
+ cancel (``state.clear()`` from ``_on_agent_run_cancel``), or end-of-turn
154
+ (``state.clear()`` from ``_on_agent_run_end``).
155
+
156
+ So only childless, not-done, genuinely-stale ORPHAN LEAVES are eligible --
157
+ a non-root row whose parent is no longer in the tree AND has been silent
158
+ longer than IDLE_PRUNE_S. End-of-turn ``state.clear()`` remains the real
159
+ cleanup for anything that errors/cancels without flushing.
160
+ """
161
+ now = time.time()
162
+ with _LOCK:
163
+ ids = set(_AGENTS)
164
+ # session_ids still referenced as someone's parent == busy (awaiting kids).
165
+ busy_parents = {e.get("parent") for e in _AGENTS.values() if e.get("parent")}
166
+ # Prune only non-done, childless, non-root orphan leaves whose parent is
167
+ # gone and whose silence exceeds IDLE_PRUNE_S; roots need explicit cleanup.
168
+ stale = [
169
+ s
170
+ for s, e in _AGENTS.items()
171
+ if not e.get("done")
172
+ and s not in busy_parents
173
+ and e.get("parent") is not None
174
+ and e.get("parent") not in ids
175
+ and now - e["last_seen"] > IDLE_PRUNE_S
176
+ ]
177
+ for s in stale:
178
+ _AGENTS.pop(s, None)
179
+ return sorted(_AGENTS.values(), key=lambda e: e["start"])
180
+
181
+
182
+ def has_active() -> bool:
183
+ with _LOCK:
184
+ return bool(_AGENTS)
185
+
186
+
187
+ # Helpers
188
+ def _derive_status(event_type: str, event_data: Any) -> Optional[str]:
189
+ """Map a sub-agent stream event to a short status. None = no change."""
190
+ if not isinstance(event_data, dict):
191
+ return None
192
+ if event_type == "part_start":
193
+ tool = event_data.get("tool_name")
194
+ if tool:
195
+ return f"calling {tool}"
196
+ part_type = event_data.get("part_type", "") or ""
197
+ if "Thinking" in part_type:
198
+ return "thinking..."
199
+ if "Text" in part_type:
200
+ return "writing response"
201
+ return None
202
+
203
+
204
+ def fmt_elapsed(start: float) -> str:
205
+ """mm:ss elapsed (2-digit minutes), e.g. 00:19."""
206
+ elapsed = int(max(0.0, time.time() - start))
207
+ return f"{elapsed // 60:02d}:{elapsed % 60:02d}"
208
+
209
+
210
+ def fmt_elapsed_entry(entry: Dict[str, Any]) -> str:
211
+ """mm:ss for an entry, frozen at its 'end' time once done."""
212
+ end = entry.get("end")
213
+ start = entry["start"]
214
+ elapsed = int(max(0.0, (end if end else time.time()) - start))
215
+ return f"{elapsed // 60:02d}:{elapsed % 60:02d}"
216
+
217
+
218
+ def spinner_frame() -> str:
219
+ """Wall-clock-derived spinner char (~10fps), independent of caller."""
220
+ return SPINNER_FRAMES[int(time.time() * 10) % len(SPINNER_FRAMES)]
221
+
222
+
223
+ def status_style(status: str) -> str:
224
+ """Color-code the status text by activity type."""
225
+ if status.startswith("calling"):
226
+ return "yellow"
227
+ if status.startswith("thinking"):
228
+ return "magenta"
229
+ if status.startswith("writing"):
230
+ return "green"
231
+ return "dim"
@@ -0,0 +1,5 @@
1
+ """Plugin: /switch-agent and /sa commands.
2
+
3
+ Switches the active agent and automatically resumes the last autosave
4
+ session that was active in this terminal window.
5
+ """
@@ -0,0 +1,314 @@
1
+ """Register /switch-agent and /sa custom commands.
2
+
3
+ /switch-agent <agent> — switch agent + auto-resume last terminal session
4
+ /switch-agent — show interactive agent picker, then switch + resume
5
+ /sa — alias for /switch-agent
6
+ """
7
+
8
+ import datetime
9
+ import os
10
+ from pathlib import Path
11
+
12
+ from code_puppy.callbacks import register_callback
13
+
14
+
15
+ def _do_switch_and_resume(agent_name: str) -> bool:
16
+ """Switch to agent_name and auto-resume this terminal's last session."""
17
+ import uuid
18
+
19
+ from rich.text import Text
20
+
21
+ from code_puppy.agents import get_current_agent, set_current_agent
22
+ from code_puppy.config import (
23
+ AUTOSAVE_DIR,
24
+ finalize_autosave_session,
25
+ get_current_session_name,
26
+ get_last_terminal_session,
27
+ pin_current_session_name,
28
+ record_terminal_session,
29
+ )
30
+ from code_puppy.messaging import emit_info, emit_success, emit_warning
31
+ from code_puppy.session_storage import (
32
+ build_session_paths,
33
+ load_session,
34
+ save_session,
35
+ )
36
+
37
+ group_id = str(uuid.uuid4())
38
+
39
+ current_agent = get_current_agent()
40
+ if current_agent.name == agent_name:
41
+ emit_info(
42
+ f"Already using agent: {current_agent.display_name}",
43
+ message_group=group_id,
44
+ )
45
+ return True
46
+
47
+ # Capture the last saved session before finalize rotates to a fresh empty ID.
48
+ last_session_to_resume = get_last_terminal_session()
49
+
50
+ # Force-save before switching so users without auto-save keep context; resume the
51
+ # conversation just saved.
52
+ try:
53
+ history = current_agent.get_message_history()
54
+ if history: # Only save if there's something to save
55
+ session_name = get_current_session_name()
56
+ # Record the terminal session mapping before saving
57
+ record_terminal_session(session_name)
58
+ # Force-save the session to disk
59
+ save_session(
60
+ history=history,
61
+ session_name=session_name,
62
+ base_dir=Path(AUTOSAVE_DIR),
63
+ timestamp=datetime.datetime.now().isoformat(),
64
+ token_estimator=current_agent.estimate_tokens_for_message,
65
+ auto_saved=True,
66
+ )
67
+ last_session_to_resume = session_name
68
+ except Exception as e:
69
+ emit_warning(
70
+ f"Failed to save session before switching: {e}. "
71
+ "Your context may not be recoverable.",
72
+ message_group=group_id,
73
+ )
74
+
75
+ # Finalize current session, switch agent
76
+ new_session_id = finalize_autosave_session()
77
+ if not set_current_agent(agent_name):
78
+ emit_warning(
79
+ "Agent switch failed after autosave rotation. Your context was preserved.",
80
+ message_group=group_id,
81
+ )
82
+ return True
83
+
84
+ new_agent = get_current_agent()
85
+ new_agent.reload_code_generation_agent()
86
+
87
+ # Auto-resume the session captured before finalize rotated/recorded a new id.
88
+ if last_session_to_resume:
89
+ try:
90
+ autosave_dir = Path(AUTOSAVE_DIR).resolve()
91
+ session_pickle = build_session_paths(
92
+ autosave_dir, last_session_to_resume
93
+ ).pickle_path.resolve()
94
+ if autosave_dir not in session_pickle.parents:
95
+ raise FileNotFoundError(session_pickle)
96
+ history = load_session(last_session_to_resume, autosave_dir)
97
+ new_agent.set_message_history(history)
98
+ pin_current_session_name(last_session_to_resume)
99
+ record_terminal_session(last_session_to_resume)
100
+ total_tokens = sum(
101
+ new_agent.estimate_tokens_for_message(m) for m in history
102
+ )
103
+ emit_success(
104
+ Text.from_markup(
105
+ f"Switched to [bold]{new_agent.display_name}[/bold] "
106
+ f"and resumed last session "
107
+ f"([dim]{len(history)} messages, {total_tokens} tokens[/dim])"
108
+ ),
109
+ message_group=group_id,
110
+ )
111
+ emit_info(f"{new_agent.description}", message_group=group_id)
112
+ emit_info(
113
+ Text.from_markup(
114
+ f"[dim]Auto-save session: {last_session_to_resume}[/dim]"
115
+ ),
116
+ message_group=group_id,
117
+ )
118
+ # Show resumed history summary
119
+ try:
120
+ from code_puppy.command_line.autosave_menu import (
121
+ display_resumed_history,
122
+ )
123
+
124
+ display_resumed_history(history)
125
+ except Exception:
126
+ pass
127
+ except Exception:
128
+ # Session file gone or any other load error — clean switch, don't crash
129
+ emit_success(
130
+ f"Switched to agent: {new_agent.display_name}",
131
+ message_group=group_id,
132
+ )
133
+ emit_info(f"{new_agent.description}", message_group=group_id)
134
+ else:
135
+ # No previous session for this terminal — fresh start
136
+ emit_success(
137
+ f"Switched to agent: {new_agent.display_name}",
138
+ message_group=group_id,
139
+ )
140
+ emit_info(f"{new_agent.description}", message_group=group_id)
141
+ emit_info(
142
+ Text.from_markup(
143
+ f"[dim]Auto-save session rotated to: {new_session_id}[/dim]"
144
+ ),
145
+ message_group=group_id,
146
+ )
147
+
148
+ return True
149
+
150
+
151
+ def _handle_switch_agent(command: str, name: str) -> object:
152
+ """Handle /switch-agent and /sa commands."""
153
+ if name not in ("switch-agent", "sa"):
154
+ return None # Not our command
155
+
156
+ from code_puppy.agents import get_available_agents
157
+ from code_puppy.command_line.agent_menu import interactive_agent_picker
158
+
159
+ tokens = command.split()
160
+
161
+ # No arg OR unrecognized agent name → interactive picker (same as /agent)
162
+ if len(tokens) == 1:
163
+ agent_name = None
164
+ else:
165
+ candidate = tokens[1].lower()
166
+ available = get_available_agents()
167
+ agent_name = candidate if candidate in available else None
168
+
169
+ if agent_name is None:
170
+ # Show interactive picker
171
+ import asyncio
172
+ import concurrent.futures
173
+
174
+ try:
175
+ with concurrent.futures.ThreadPoolExecutor() as executor:
176
+ future = executor.submit(
177
+ lambda: asyncio.run(interactive_agent_picker())
178
+ )
179
+ agent_name = future.result(timeout=300)
180
+ except Exception:
181
+ agent_name = None
182
+
183
+ # Apply picker-deferred pin reloads on the main loop; worker-thread reloads
184
+ # can deadlock MCP autostart during loop shutdown.
185
+ try:
186
+ from code_puppy.command_line.agent_menu import (
187
+ apply_pending_pin_reload,
188
+ consume_pending_pin_reloads,
189
+ )
190
+
191
+ for pin_agent, pin_model in consume_pending_pin_reloads():
192
+ apply_pending_pin_reload(pin_agent, pin_model)
193
+ except Exception:
194
+ pass
195
+
196
+ if not agent_name:
197
+ from code_puppy.messaging import emit_warning
198
+
199
+ emit_warning("Agent selection cancelled")
200
+ return True
201
+
202
+ return _do_switch_and_resume(agent_name)
203
+
204
+
205
+ def _handle_help() -> list:
206
+ return [
207
+ (
208
+ "switch-agent",
209
+ "Switch agent + auto-resume last terminal session (alias: /sa)",
210
+ ),
211
+ ]
212
+
213
+
214
+ def _cleanup_orphaned_tty_sessions() -> None:
215
+ """Clean up orphaned TTY session files on startup.
216
+
217
+ Removes session files for:
218
+ 1. TTY devices that no longer exist (terminal closed)
219
+ 2. Files older than 7 days (stale sessions)
220
+
221
+ Never crashes — all errors are silently caught.
222
+ """
223
+ from code_puppy.config import CACHE_DIR, get_terminal_tty
224
+
225
+ try:
226
+ # Skip cleanup if no TTY available
227
+ current_tty = get_terminal_tty()
228
+ if not current_tty:
229
+ return
230
+
231
+ # Get the tty_sessions directory
232
+ tty_sessions_dir = Path(CACHE_DIR) / "tty_sessions"
233
+ if not tty_sessions_dir.exists():
234
+ return
235
+
236
+ # Build current terminal's filename to skip it
237
+ current_tty_key = current_tty.replace("/", "_").lstrip("_")
238
+ current_filename = f"{current_tty_key}.txt"
239
+
240
+ # Calculate cutoff time (7 days ago)
241
+ cutoff_time = datetime.datetime.now() - datetime.timedelta(days=7)
242
+
243
+ # Iterate through all .txt files in tty_sessions directory
244
+ for session_file in tty_sessions_dir.glob("*.txt"):
245
+ try:
246
+ # Skip current terminal's file
247
+ if session_file.name == current_filename:
248
+ continue
249
+
250
+ # Rebuild the TTY path from the session filename (e.g. dev_ttys057 ->
251
+ # /dev/ttys057).
252
+ tty_key = session_file.stem # Remove .txt extension
253
+ tty_path = "/" + tty_key.replace("_", "/")
254
+
255
+ # Delete if TTY device no longer exists
256
+ if not os.path.exists(tty_path):
257
+ session_file.unlink()
258
+ continue
259
+
260
+ # Delete if file is older than 7 days
261
+ file_mtime = datetime.datetime.fromtimestamp(
262
+ session_file.stat().st_mtime
263
+ )
264
+ if file_mtime < cutoff_time:
265
+ session_file.unlink()
266
+
267
+ except Exception:
268
+ # Skip this file on any error, never crash
269
+ continue
270
+
271
+ except Exception:
272
+ # Never crash the app over cleanup
273
+ pass
274
+
275
+
276
+ async def _cleanup_orphaned_tty_sessions_async() -> None:
277
+ """Non-blocking wrapper for TTY session cleanup on startup.
278
+
279
+ Fires off the cleanup task in a background thread and immediately returns,
280
+ ensuring zero impact on Code Puppy launch time.
281
+ """
282
+ import asyncio
283
+
284
+ # Fire-and-forget: run cleanup in background thread without blocking startup
285
+ asyncio.create_task(asyncio.to_thread(_cleanup_orphaned_tty_sessions))
286
+
287
+
288
+ def _has_user_override() -> bool:
289
+ """Return True if a user-level switch_agent_resume plugin exists.
290
+
291
+ Built-in plugins load before user plugins. The callback wrapper below
292
+ returns ``None`` when a user plugin exists so user code can take ownership
293
+ of /switch-agent and /sa without racing the built-in handler.
294
+ """
295
+ user_plugin_file = (
296
+ Path.home()
297
+ / ".code_puppy"
298
+ / "plugins"
299
+ / "switch_agent_resume"
300
+ / "register_callbacks.py"
301
+ )
302
+ return user_plugin_file.exists()
303
+
304
+
305
+ def _handle_switch_agent_callback(command: str, name: str) -> object:
306
+ """Callback wrapper that yields ownership to a user override plugin."""
307
+ if _has_user_override():
308
+ return None
309
+ return _handle_switch_agent(command, name)
310
+
311
+
312
+ register_callback("startup", _cleanup_orphaned_tty_sessions_async)
313
+ register_callback("custom_command", _handle_switch_agent_callback)
314
+ register_callback("custom_command_help", _handle_help)
@@ -0,0 +1,86 @@
1
+ # theme — `/theme` for Code Puppy
2
+
3
+ A friendlier `/theme` command with an **interactive picker**, **live preview**,
4
+ and **four layers of theming** — banner headers, body text, inline markup,
5
+ AND the whole terminal window background/foreground/ANSI palette via OSC
6
+ escape sequences (mure-style).
7
+
8
+ ## What you get
9
+
10
+ ```
11
+ /theme
12
+ ```
13
+ Launches a paginated split-panel TUI with 16 themes (7 neon/dark themes, 7 palette-first/light themes, surprise, reset). Five themes are shown per page; use Up/Down for item navigation and PgUp/PgDn to change pages.
14
+
15
+
16
+ ## What gets themed
17
+
18
+ | Level | Surface | Themed? |
19
+ |-------|---------|---------|
20
+ | 0 | Banner headers (`[ THINKING ]`, etc.) | ✅ |
21
+ | 1 | Body text (`emit_info`/`warning`/`success`/`error`/`debug` + diffs) | ✅ |
22
+ | 2 | Inline Rich markup (`[bold cyan]`, `[dim cyan]`, `[magenta]`...) | ✅ |
23
+ | 3 | **Entire terminal window** (bg/fg/16-color ANSI palette via OSC 10/11/4) | ✅ |
24
+
25
+ **Semantic colors are preserved** at Level 2 (red errors stay angry, yellow warnings warn). Level 3 OSC remap is broader — it changes how *your terminal itself* interprets every ANSI color, so e.g. an `ls` ran after `/theme tokyo-night` will also look Tokyo Night. Pure terminal-level magic.
26
+
27
+ Tokyo Night is applied by default on first run. Themes persist to your Code Puppy config and survive restarts; existing or explicitly restored choices are never overwritten by the default. The OSC palette auto-resets on Code Puppy exit (via `atexit`) so you never get a stuck-pink terminal.
28
+
29
+ ## Bundled themes
30
+
31
+ | # | Theme | Vibe |
32
+ |---|-------|------|
33
+ | 1 | 🌊 Ocean | cool blues, cyans & teals |
34
+ | 2 | 🌲 Forest | earthy greens & olives |
35
+ | 3 | 🌅 Sunset | warm reds, oranges & gold |
36
+ | 4 | 🪩 Vaporwave | neon pinks & purples |
37
+ | 5 | 🫧 Bubblegum Pink | sweet pinks, candy neon & glow |
38
+ | 6 | 🐶 Purple Puppy | vivid violet fur, white muzzle & pink tongue |
39
+ | 7 | 🐱 Catppuccin Mocha | soothing pastel dark (mure default) |
40
+ | 8 | ☕ Catppuccin Latte | soothing pastel light |
41
+ | 9 | 🌃 Tokyo Night | neon-on-navy night |
42
+ | 10 | [CRT] Green Screen | llxprt's radioactive green phosphor CRT |
43
+ | 11 | Deep Black | inky noir with subtle neon edge |
44
+ | 12 | Solarized Light | classic warm beige with calm accents |
45
+ | 13 | GitHub Light | crisp white, familiar code colors |
46
+ | 14 | Rose Pine Dawn | soft pastel rose light |
47
+ | 15 | Surprise Me | a fresh random remix every time |
48
+ | 16 | Restore Defaults | back to Code Puppy + terminal factory |
49
+
50
+ Green Screen ports the canonical phosphor colors from [vybestack/llxprt-code](https://github.com/vybestack/llxprt-code), whose theme is licensed under Apache-2.0.
51
+
52
+ ## Power-user shortcuts
53
+
54
+ ```
55
+ /theme 5 apply theme #5 (Bubblegum Pink)
56
+ /theme bubblegum apply by alias (also: pink, puppy, purple, mocha,
57
+ latte, tokyo, green, crt, solarized, github, rose-pine)
58
+ /theme tokyo-night apply by canonical name
59
+ /theme surprise re-roll a random palette
60
+ /theme default restore Code Puppy + terminal factory colors
61
+ /theme reset alias of /theme default
62
+ /theme show dump current banner + content style mappings
63
+ ```
64
+
65
+ ## Architecture (Zen-compliant, one job per file)
66
+
67
+ | File | Responsibility |
68
+ |------|----------------|
69
+ | `themes.py` | Theme catalog + banner/content/remap/palette construction |
70
+ | `bundled_palettes.py` | Hex palette data (Catppuccin, Tokyo Night, Deep Black, +5 originals) |
71
+ | `content_styles.py` | Mutates `RichConsoleRenderer` style maps (Level 1) |
72
+ | `rich_themes.py` | Monkey-patches `Console.get_style()` for inline remap (Level 2) |
73
+ | `osc_palette.py` | OSC escape sequences for terminal-wide bg/fg/ANSI palette (Level 3) |
74
+ | `picker.py` | prompt_toolkit split-panel TUI with live preview |
75
+ | `register_callbacks.py` | `/theme` command handler, glue only |
76
+ | `__init__.py` | Re-applies persisted (L1 + L2 + L3) on plugin load |
77
+
78
+ ## How each level works (the spicy bits)
79
+
80
+ **Level 1** — mutates `code_puppy.messaging.rich_renderer.DEFAULT_STYLES` and walks live `RichConsoleRenderer._styles` via `gc.get_objects()`.
81
+
82
+ **Level 2** — Rich's stock `Theme` only intercepts named styles, not base colors inside `[bold cyan]`. So we monkey-patch `Console.get_style()` on every live `Console`: every resolved `Style` flows throughd if `.color`/`.bgcolor` matches our remap, we swap. Tracked in a `WeakKeyDictionary` for clean swap-without-leak.
83
+
84
+ **Level 3** — fires xterm OSC sequences (`\\033]10;#fg\\007`, `\\033]11;#bg\\007`, `\\033]4;N;#hex\\007`) which modern terminals honor terminal-wide. Auto-resets on exit via `atexit`. Persists across restarts via JSON in `puppy.cfg`. Supported in iTerm2, Terminal.app, Alacritty, kitty, ode, GNOME Terminal, Windows Terminal. Unsupported terminals silently ignore.
85
+
86
+ Plays nice with `/colors` — same color pool, same config keys for banners.
@@ -0,0 +1,14 @@
1
+ """Theme picker plugin for Code Puppy - banner + content + inline + terminal theming."""
2
+
3
+ # Reapply persisted overrides at load: banner colors persist in puppy.cfg, while
4
+ # content styles, Rich remaps, and OSC palettes reset each process.
5
+ from . import content_styles as _cs
6
+ from . import osc_palette as _osc
7
+ from . import rich_themes as _rt
8
+
9
+ for _mod in (_cs, _rt, _osc):
10
+ try:
11
+ _mod.reapply_from_config()
12
+ except Exception:
13
+ # Never let theme persistence break Code Puppy startup.
14
+ pass