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 @@
1
+ """Timestamp heartbeat plugin — see register_callbacks for details."""
@@ -0,0 +1,114 @@
1
+ """Plugin: timestamp heartbeat — stamp the current datetime into tool results.
2
+
3
+ Models are time-blind: nothing in a long agent run tells them how much
4
+ wall-clock time has passed. Every N tool calls this plugin injects a
5
+ ``__SYS_TIMESTAMP__`` key into the tool's result dict so the model gets a
6
+ periodic clock tick without any extra transcript noise or history messages.
7
+
8
+ How the injection works (and why it's safe):
9
+ ``pydantic_patches._patched_call_tool`` does ``return result`` inside a
10
+ ``try`` block and awaits ``on_post_tool_call(..., result, ...)`` in the
11
+ ``finally``. Python evaluates the return value (a *reference* to the
12
+ dict) before running ``finally``, so an **in-place** mutation of that
13
+ dict here is visible to the caller — and therefore to the model when
14
+ the result is serialized into the ToolReturnPart.
15
+
16
+ Corollary: only plain ``dict`` results can be stamped. When the k-th
17
+ call returns something else (a string, a pydantic model), the stamp is
18
+ marked *due* and lands on the next dict-shaped result instead of being
19
+ silently dropped.
20
+
21
+ Config (puppy.cfg):
22
+ timestamp_heartbeat_interval = 10 # stamp every N tool calls; 0 disables
23
+ """
24
+
25
+ from __future__ import annotations
26
+
27
+ import logging
28
+ from datetime import datetime
29
+ from typing import Any
30
+
31
+ from code_puppy.callbacks import register_callback
32
+
33
+ logger = logging.getLogger(__name__)
34
+
35
+ TIMESTAMP_KEY = "__SYS_TIMESTAMP__"
36
+ CONFIG_KEY = "timestamp_heartbeat_interval"
37
+ DEFAULT_INTERVAL = 10
38
+
39
+ # Module-level state. Tool calls are dispatched on the event loop, so plain
40
+ # ints are fine — no locking theater needed.
41
+ _calls_since_stamp = 0
42
+ _stamp_due = False
43
+
44
+
45
+ def _get_interval() -> int:
46
+ """Read the heartbeat interval from puppy.cfg (0 or negative disables)."""
47
+ try:
48
+ from code_puppy.config import get_value
49
+
50
+ raw = get_value(CONFIG_KEY)
51
+ if raw is None or str(raw).strip() == "":
52
+ return DEFAULT_INTERVAL
53
+ return int(str(raw).strip())
54
+ except (ValueError, TypeError):
55
+ logger.warning(
56
+ "Invalid %s value; falling back to %d", CONFIG_KEY, DEFAULT_INTERVAL
57
+ )
58
+ return DEFAULT_INTERVAL
59
+ except Exception:
60
+ return DEFAULT_INTERVAL
61
+
62
+
63
+ def _now_stamp() -> str:
64
+ """Local time with UTC offset, second precision — readable and unambiguous."""
65
+ return datetime.now().astimezone().isoformat(timespec="seconds")
66
+
67
+
68
+ def _reset_state() -> None:
69
+ """Reset the counter/due flag (used by tests and defensive re-init)."""
70
+ global _calls_since_stamp, _stamp_due
71
+ _calls_since_stamp = 0
72
+ _stamp_due = False
73
+
74
+
75
+ def _on_post_tool_call(
76
+ tool_name: str,
77
+ tool_args: dict,
78
+ result: Any,
79
+ duration_ms: float,
80
+ context: Any = None,
81
+ ) -> None:
82
+ """Count tool calls; every ``interval`` calls, stamp the result dict."""
83
+ global _calls_since_stamp, _stamp_due
84
+ _ = tool_name, tool_args, duration_ms, context # signature conformance
85
+
86
+ try:
87
+ interval = _get_interval()
88
+ if interval <= 0:
89
+ return
90
+
91
+ _calls_since_stamp += 1
92
+ if _calls_since_stamp >= interval:
93
+ _stamp_due = True
94
+ _calls_since_stamp = 0
95
+
96
+ if _stamp_due and isinstance(result, dict):
97
+ result[TIMESTAMP_KEY] = _now_stamp()
98
+ _stamp_due = False
99
+ except Exception:
100
+ # A clock tick is never worth breaking a tool call.
101
+ logger.debug("timestamp heartbeat failed", exc_info=True)
102
+
103
+
104
+ register_callback("post_tool_call", _on_post_tool_call)
105
+
106
+
107
+ __all__ = [
108
+ "CONFIG_KEY",
109
+ "DEFAULT_INTERVAL",
110
+ "TIMESTAMP_KEY",
111
+ "_get_interval",
112
+ "_on_post_tool_call",
113
+ "_reset_state",
114
+ ]
@@ -0,0 +1,5 @@
1
+ """Token ratio learner plugin — per-model chars/token ratio learning.
2
+
3
+ Provides ``count_tokens()`` using learned ratios, falling back to a safe
4
+ overestimate when no ratio has been learned yet for a given model.
5
+ """
@@ -0,0 +1,208 @@
1
+ """Learned token-per-character ratios for accurate token estimation.
2
+
3
+ When the API reports real prompt_tokens we can compute the actual
4
+ chars-per-token ratio for that model and store it keyed by the bare
5
+ model name (the part after ``:``). Future ``count_tokens`` calls use the
6
+ stored ratio for that model, falling back to a hardcoded default.
7
+
8
+ The default (2.5) matches the classic char/token heuristic used across
9
+ most models. Real tokenizers vary (GPT-4 ~3.5, Claude ~2.8), but 2.5
10
+ provides a reasonable middle ground. The max clamp (3.5) prevents
11
+ absurdly high ratios from inflating token estimates.
12
+
13
+ Storage lives at ``~/.code_puppy/token_ratios.json``, overridable via
14
+ the env var ``CODE_PUPPY_TOKEN_RATIOS_PATH``.
15
+ """
16
+
17
+ import json
18
+ import logging
19
+ import os
20
+ import tempfile
21
+ import threading
22
+ from pathlib import Path
23
+
24
+ logger = logging.getLogger(__name__)
25
+
26
+ # Constants
27
+
28
+ # Default 2.5 chars/token slightly overestimates for safer compaction.
29
+ _DEFAULT_RATIO = 2.5
30
+
31
+ # Clamp learned ratios to [1.5, 3.5]; extremes imply implausible tokenization and
32
+ # can collapse estimates or risk context overflow.
33
+ _MIN_RATIO = 1.5
34
+ _MAX_RATIO = 3.5
35
+
36
+ # Lazy in-memory cache, populated by ``_ensure_ratios_loaded()``.
37
+
38
+ _LEARNED_RATIOS: dict[str, float] = {}
39
+ _ratios_loaded: bool = False
40
+ _ratios_lock = threading.Lock()
41
+
42
+ # Learned-ratio storage path.
43
+
44
+ _TOKEN_RATIOS_PATH: Path = Path(
45
+ os.path.expanduser(
46
+ os.environ.get(
47
+ "CODE_PUPPY_TOKEN_RATIOS_PATH",
48
+ str(Path.home() / ".code_puppy" / "token_ratios.json"),
49
+ )
50
+ )
51
+ )
52
+
53
+
54
+ # Internal helpers
55
+
56
+
57
+ def _ensure_ratios_loaded() -> None:
58
+ """Populate ``_LEARNED_RATIOS`` from disk on first call (idempotent)."""
59
+ global _ratios_loaded
60
+ with _ratios_lock:
61
+ if _ratios_loaded:
62
+ return
63
+ _LEARNED_RATIOS.clear()
64
+ _LEARNED_RATIOS.update(_load_learned_ratios())
65
+ _ratios_loaded = True
66
+
67
+
68
+ def _load_learned_ratios() -> dict[str, float]:
69
+ """Load learned token ratios from the on-disk JSON file.
70
+
71
+ Returns an empty dict when the file doesn't exist or is corrupt —
72
+ callers fall back to ``_DEFAULT_RATIO``.
73
+
74
+ Each loaded value is clamped to ``[_MIN_RATIO, _MAX_RATIO]`` so that
75
+ poisoned or corrupted values cannot cause token-count collapse.
76
+ """
77
+ try:
78
+ if _TOKEN_RATIOS_PATH.is_file():
79
+ data = json.loads(_TOKEN_RATIOS_PATH.read_text(encoding="utf-8"))
80
+ if isinstance(data, dict):
81
+ clamped: dict[str, float] = {}
82
+ for k, v in data.items():
83
+ if isinstance(v, (int, float)) and v > 0:
84
+ # Lowercase keys for case-insensitive matching.
85
+ clamped[k.lower()] = max(_MIN_RATIO, min(_MAX_RATIO, float(v)))
86
+ return clamped
87
+ except (OSError, json.JSONDecodeError, ValueError):
88
+ pass
89
+ return {}
90
+
91
+
92
+ def _save_learned_ratios(ratios: dict[str, float]) -> None:
93
+ """Persist learned token ratios to disk atomically (best-effort, never raises)."""
94
+ try:
95
+ parent = _TOKEN_RATIOS_PATH.parent
96
+ parent.mkdir(parents=True, exist_ok=True)
97
+ fd, tmp_name = tempfile.mkstemp(dir=parent, suffix=".tmp")
98
+ try:
99
+ with os.fdopen(fd, "w", encoding="utf-8") as tmp:
100
+ json.dump(ratios, tmp, indent=2)
101
+ os.replace(tmp_name, str(_TOKEN_RATIOS_PATH))
102
+ except OSError:
103
+ try:
104
+ os.unlink(tmp_name)
105
+ except OSError:
106
+ pass
107
+ raise
108
+ except OSError:
109
+ pass # Silently ignore write failures (permissions, disk full, etc.)
110
+
111
+
112
+ def _record_token_ratio(model: str, char_count: int, token_count: int) -> None:
113
+ """Record an observed chars-per-token ratio for *model*.
114
+
115
+ Called after a successful API response where we know both the input
116
+ character count and the actual prompt token count. Stores a running
117
+ average keyed by the bare model name (the part after ``:``) so that
118
+ the same model accessed through different providers shares the estimate.
119
+
120
+ The running average uses a 70/30 blend: 70% old, 30% new. This lets
121
+ the estimate adapt to the real tokenizer without swinging wildly on
122
+ every call.
123
+ """
124
+ if char_count <= 0 or token_count <= 0:
125
+ return
126
+
127
+ # Strip the provider prefix and lowercase the model name.
128
+ model_name = model.split(":", 1)[1] if ":" in model else model
129
+ model_name = model_name.lower()
130
+
131
+ _ensure_ratios_loaded()
132
+
133
+ new_ratio = char_count / token_count
134
+ new_ratio = max(_MIN_RATIO, min(_MAX_RATIO, new_ratio))
135
+
136
+ with _ratios_lock:
137
+ old = _LEARNED_RATIOS.get(model_name)
138
+ if old is not None:
139
+ blended = 0.7 * old + 0.3 * new_ratio
140
+ else:
141
+ blended = new_ratio
142
+ _LEARNED_RATIOS[model_name] = round(blended, 4)
143
+ _save_learned_ratios(_LEARNED_RATIOS)
144
+
145
+
146
+ # Public API
147
+
148
+
149
+ def count_tokens(text: str, model: str | None = None) -> int:
150
+ """Estimate token count using a character-based heuristic.
151
+
152
+ If we have a *learned* chars-per-token ratio for *model*, use it.
153
+ Otherwise fall back to ``_DEFAULT_RATIO`` (2.5).
154
+
155
+ Args:
156
+ text: The text content whose token count we need.
157
+ model: Optional model identifier string (e.g.
158
+ ``"anthropic:claude-sonnet"``). The provider prefix is
159
+ stripped before lookup, so ``"wafer:glm5.1"`` and bare
160
+ ``"glm5.1"`` resolve to the same key.
161
+
162
+ Returns:
163
+ Estimated token count (always >= 1 for non-empty input).
164
+ """
165
+ _ensure_ratios_loaded()
166
+
167
+ if not text:
168
+ return 0
169
+
170
+ # Look up by bare model name (strip provider prefix, lowercase).
171
+ model_name = None
172
+ if model:
173
+ model_name = model.split(":", 1)[1] if ":" in model else model
174
+ model_name = model_name.lower()
175
+
176
+ raw_ratio = _LEARNED_RATIOS.get(model_name or "", _DEFAULT_RATIO)
177
+ ratio = max(_MIN_RATIO, min(_MAX_RATIO, raw_ratio))
178
+
179
+ result = round(len(text) / ratio)
180
+ return result if result > 0 else 1
181
+
182
+
183
+ def get_ratio_for_model(model: str) -> float:
184
+ """Return the current chars/token ratio for *model* (or default)."""
185
+ _ensure_ratios_loaded()
186
+ model_name = model.split(":", 1)[1] if ":" in model else model
187
+ model_name = model_name.lower()
188
+ raw = _LEARNED_RATIOS.get(model_name, _DEFAULT_RATIO)
189
+ return max(_MIN_RATIO, min(_MAX_RATIO, raw))
190
+
191
+
192
+ def list_known_ratios() -> dict[str, float]:
193
+ """Return a copy of all learned ratios (for debugging/stats)."""
194
+ _ensure_ratios_loaded()
195
+ with _ratios_lock:
196
+ return dict(_LEARNED_RATIOS)
197
+
198
+
199
+ # Storage path override (for tests)
200
+
201
+
202
+ def set_ratios_path(path: str | Path) -> None:
203
+ """Override the token ratios file path (mostly for testing)."""
204
+ global _TOKEN_RATIOS_PATH, _ratios_loaded, _LEARNED_RATIOS
205
+ _TOKEN_RATIOS_PATH = Path(path)
206
+ with _ratios_lock:
207
+ _LEARNED_RATIOS.clear()
208
+ _ratios_loaded = False
@@ -0,0 +1,213 @@
1
+ """Register callbacks for the token-ratio-learner plugin.
2
+
3
+ On startup we monkeypatch four things:
4
+
5
+ 1. ``_history.estimate_tokens`` — replaces the hardcoded
6
+ ``max(1, floor(len(text)/2.5))`` with a learned-ratio lookup, falling
7
+ back to ``_DEFAULT_RATIO`` (2.5) which matches the classic heuristic.
8
+
9
+ 2. ``_history.estimate_tokens_for_message`` — replaces per-part char/2.5
10
+ summing + ``model_token_multiplier`` with direct
11
+ ``ratios.count_tokens(part_str, model=model_name)`` per part. The
12
+ learned ratio is already model-calibrated, so the multiplier becomes
13
+ redundant.
14
+
15
+ 3. ``_runtime.run_with_mcp`` — wraps the orchestration so that after every
16
+ successful agent run we extract ``result.usage().input_tokens`` and
17
+ the character count of the input, then call
18
+ ``ratios._record_token_ratio()`` to update the running average.
19
+
20
+ 4. ``subagent_stream_handler._estimate_tokens`` — same replacement as
21
+ (1) so that subagent streaming token counts use learned ratios.
22
+
23
+ We do **not** touch ``event_stream_handler`` inlining — that code is for
24
+ progress display only, not for compaction decisions.
25
+ """
26
+
27
+ import logging
28
+ from typing import Any, Optional, Union
29
+
30
+ from code_puppy.callbacks import register_callback
31
+
32
+ logger = logging.getLogger(__name__)
33
+
34
+ # Original references, populated during startup.
35
+
36
+ _original_estimate_tokens = None
37
+ _original_estimate_tokens_for_message = None
38
+ _original_run_with_mcp = None
39
+ _original_subagent_estimate_tokens = None
40
+
41
+
42
+ # Patched ``estimate_tokens`` (root text-level)
43
+
44
+
45
+ def _patched_estimate_tokens(text: str) -> int:
46
+ """Drop-in replacement that uses learned char/token ratios.
47
+
48
+ No model argument — uses the default ratio. Callers that need
49
+ model-specific calibration go through ``estimate_tokens_for_message``.
50
+ """
51
+ from code_puppy_core_plugins.token_ratio_learner.ratios import count_tokens
52
+
53
+ return count_tokens(text, model=None)
54
+
55
+
56
+ # Patched ``estimate_tokens_for_message``
57
+
58
+
59
+ def _patched_estimate_tokens_for_message(
60
+ message: Any,
61
+ model_name: Optional[str] = None,
62
+ ) -> int:
63
+ """Per-message token estimation using learned model-specific ratios.
64
+
65
+ Iterates over ``message.parts``, stringifying each with
66
+ ``stringify_part``, then calls ``ratios.count_tokens(part_str, model)``.
67
+ Bypasses the old ``estimate_tokens`` + ``model_token_multiplier`` chain
68
+ entirely — the learned ratio *is* the calibration.
69
+ """
70
+ # Import stringify_part from the same module the original lives in
71
+ from code_puppy.agents._history import stringify_part
72
+ from code_puppy_core_plugins.token_ratio_learner.ratios import count_tokens
73
+
74
+ total = 0
75
+ for part in getattr(message, "parts", []) or []:
76
+ part_str = stringify_part(part)
77
+ if part_str:
78
+ total += count_tokens(part_str, model=model_name)
79
+ return max(1, total)
80
+
81
+
82
+ # Patched ``run_with_mcp`` records ratios after each API call.
83
+
84
+
85
+ def _compute_input_char_count(
86
+ agent: Any, prompt: Union[str, list], attachments: Any, link_attachments: Any
87
+ ) -> int:
88
+ """Compute a reasonable character count for the input to the model.
89
+
90
+ Counts the prompt text (or list items) plus the stringified message
91
+ history. Binary/URL attachments aren't tokenized as text so they're
92
+ skipped.
93
+ """
94
+ char_count = 0
95
+
96
+ if isinstance(prompt, str):
97
+ char_count += len(prompt)
98
+ elif isinstance(prompt, list):
99
+ for item in prompt:
100
+ if isinstance(item, str):
101
+ char_count += len(item)
102
+
103
+ try:
104
+ history = agent._message_history
105
+ from code_puppy.agents._history import stringify_part
106
+
107
+ for msg in history:
108
+ for part in getattr(msg, "parts", []) or []:
109
+ part_str = stringify_part(part)
110
+ if part_str:
111
+ char_count += len(part_str)
112
+ except Exception:
113
+ pass
114
+
115
+ return char_count
116
+
117
+
118
+ async def _patched_run_with_mcp(
119
+ agent: Any,
120
+ prompt: str,
121
+ *,
122
+ attachments: Optional[Any] = None,
123
+ link_attachments: Optional[Any] = None,
124
+ output_type: Optional[Any] = None,
125
+ **kwargs: Any,
126
+ ) -> Any:
127
+ """Wrap ``run_with_mcp`` to learn token ratios from real API responses."""
128
+ global _original_run_with_mcp
129
+
130
+ input_char_count = _compute_input_char_count(
131
+ agent, prompt, attachments, link_attachments
132
+ )
133
+
134
+ result = await _original_run_with_mcp(
135
+ agent,
136
+ prompt,
137
+ attachments=attachments,
138
+ link_attachments=link_attachments,
139
+ output_type=output_type,
140
+ **kwargs,
141
+ )
142
+
143
+ # Try to extract token usage from the result
144
+ try:
145
+ usage = result.usage() # AgentRunResult.usage() → RunUsage
146
+ input_tokens = getattr(usage, "input_tokens", 0) or getattr(
147
+ usage, "request_tokens", 0
148
+ )
149
+ if input_tokens > 0 and input_char_count > 0:
150
+ from code_puppy_core_plugins.token_ratio_learner.ratios import (
151
+ _record_token_ratio,
152
+ )
153
+
154
+ model = agent.get_model_name()
155
+ if model:
156
+ _record_token_ratio(model, input_char_count, input_tokens)
157
+ except Exception:
158
+ pass # Never let ratio recording break the agent run
159
+
160
+ return result
161
+
162
+
163
+ # Patched subagent ``_estimate_tokens``
164
+
165
+
166
+ def _patched_subagent_estimate_tokens(content: str) -> int:
167
+ """Drop-in replacement for subagent token estimation."""
168
+ from code_puppy_core_plugins.token_ratio_learner.ratios import count_tokens
169
+
170
+ return count_tokens(content, model=None)
171
+
172
+
173
+ # Startup hook
174
+
175
+
176
+ def _on_startup() -> None:
177
+ """Apply all monkeypatches on startup."""
178
+ global _original_estimate_tokens, _original_estimate_tokens_for_message
179
+ global _original_run_with_mcp, _original_subagent_estimate_tokens
180
+
181
+ # 1. Patch _history.estimate_tokens (root text-level)
182
+ from code_puppy.agents import _history
183
+
184
+ _original_estimate_tokens = _history.estimate_tokens
185
+ _history.estimate_tokens = _patched_estimate_tokens
186
+ logger.info("token_ratio_learner: patched _history.estimate_tokens")
187
+
188
+ # 2. Patch _history.estimate_tokens_for_message
189
+ _original_estimate_tokens_for_message = _history.estimate_tokens_for_message
190
+ _history.estimate_tokens_for_message = _patched_estimate_tokens_for_message
191
+ logger.info("token_ratio_learner: patched _history.estimate_tokens_for_message")
192
+
193
+ # 3. Patch _runtime.run_with_mcp
194
+ from code_puppy.agents import _runtime
195
+
196
+ _original_run_with_mcp = _runtime.run_with_mcp
197
+ _runtime.run_with_mcp = _patched_run_with_mcp
198
+ logger.info("token_ratio_learner: patched _runtime.run_with_mcp")
199
+
200
+ # 4. Patch subagent_stream_handler._estimate_tokens
201
+ # NOTE: ``subagent_stream_handler`` re-exports a function, not its module;
202
+ # import the actual module via ``importlib`` before patching.
203
+ import importlib
204
+
205
+ ssh_mod = importlib.import_module("code_puppy.agents.subagent_stream_handler")
206
+
207
+ _original_subagent_estimate_tokens = ssh_mod._estimate_tokens
208
+ ssh_mod._estimate_tokens = _patched_subagent_estimate_tokens
209
+ logger.info("token_ratio_learner: patched subagent_stream_handler._estimate_tokens")
210
+
211
+
212
+ # Register the startup callback
213
+ register_callback("startup", _on_startup)
@@ -0,0 +1,13 @@
1
+ """Universal Constructor - Dynamic tool creation and management plugin.
2
+
3
+ This plugin enables users to create, manage, and deploy custom tools
4
+ that extend Code Puppy's capabilities. Tools are stored in the user's
5
+ config directory and can be organized into namespaces via subdirectories.
6
+ """
7
+
8
+ from pathlib import Path
9
+
10
+ # User tools directory - where user-created UC tools live
11
+ USER_UC_DIR = Path.home() / ".code_puppy" / "plugins" / "universal_constructor"
12
+
13
+ __all__ = ["USER_UC_DIR"]
@@ -0,0 +1,138 @@
1
+ """Pydantic models for Universal Constructor tools and responses.
2
+
3
+ This module defines the data structures used throughout the UC plugin
4
+ for representing tool metadata, tool information, and operation responses.
5
+ """
6
+
7
+ from typing import Any, List, Optional
8
+
9
+ from pydantic import BaseModel, Field
10
+
11
+
12
+ class ToolMeta(BaseModel):
13
+ """Metadata for a UC tool.
14
+
15
+ This is the structure expected in the TOOL_META dictionary
16
+ at the top of each tool file.
17
+ """
18
+
19
+ name: str = Field(..., description="Human-readable tool name")
20
+ namespace: str = Field(
21
+ default="", description="Namespace for the tool (from subdirectory path)"
22
+ )
23
+ description: str = Field(..., description="What the tool does")
24
+ enabled: bool = Field(default=True, description="Whether the tool is active")
25
+ version: str = Field(default="1.0.0", description="Semantic version of the tool")
26
+ author: str = Field(default="", description="Tool author or creator")
27
+ created_at: Optional[str] = Field(
28
+ default=None, description="When the tool was created (ISO format string)"
29
+ )
30
+
31
+ model_config = {"extra": "allow"} # Allow additional metadata fields
32
+
33
+
34
+ class UCToolInfo(BaseModel):
35
+ """Full information about a UC tool.
36
+
37
+ Combines metadata with runtime information like function signature
38
+ and source file location.
39
+ """
40
+
41
+ meta: ToolMeta = Field(..., description="Tool metadata")
42
+ signature: str = Field(..., description="Function signature string")
43
+ source_path: str = Field(..., description="Path to the tool source file")
44
+ function_name: str = Field(default="", description="Name of the callable function")
45
+ docstring: Optional[str] = Field(default=None, description="Function docstring")
46
+
47
+ model_config = {"arbitrary_types_allowed": True}
48
+
49
+ @property
50
+ def full_name(self) -> str:
51
+ """Get the fully qualified tool name including namespace."""
52
+ if self.meta.namespace:
53
+ return f"{self.meta.namespace}.{self.meta.name}"
54
+ return self.meta.name
55
+
56
+
57
+ # Response models for UC operations
58
+
59
+
60
+ class UCListOutput(BaseModel):
61
+ """Response model for listing UC tools."""
62
+
63
+ tools: List[UCToolInfo] = Field(
64
+ default_factory=list, description="List of available tools"
65
+ )
66
+ total_count: int = Field(default=0, description="Total number of tools")
67
+ enabled_count: int = Field(default=0, description="Number of enabled tools")
68
+ error: Optional[str] = Field(default=None, description="Error message if any")
69
+
70
+ model_config = {"arbitrary_types_allowed": True}
71
+
72
+
73
+ class UCCallOutput(BaseModel):
74
+ """Response model for calling a UC tool."""
75
+
76
+ success: bool = Field(..., description="Whether the call succeeded")
77
+ tool_name: str = Field(..., description="Name of the tool that was called")
78
+ result: Any = Field(default=None, description="Return value from the tool")
79
+ error: Optional[str] = Field(default=None, description="Error message if failed")
80
+ execution_time: Optional[float] = Field(
81
+ default=None, description="Execution time in seconds"
82
+ )
83
+ source_preview: Optional[str] = Field(
84
+ default=None, description="Preview of the tool's source code that was executed"
85
+ )
86
+
87
+
88
+ class UCCreateOutput(BaseModel):
89
+ """Response model for creating a UC tool."""
90
+
91
+ success: bool = Field(..., description="Whether creation succeeded")
92
+ tool_name: str = Field(default="", description="Name of the created tool")
93
+ source_path: Optional[str] = Field(
94
+ default=None, description="Path where tool was saved"
95
+ )
96
+ preview: Optional[str] = Field(
97
+ default=None, description="Preview of the first 10 lines of source code"
98
+ )
99
+ error: Optional[str] = Field(default=None, description="Error message if failed")
100
+ validation_warnings: List[str] = Field(
101
+ default_factory=list, description="Non-fatal validation warnings"
102
+ )
103
+
104
+ model_config = {"arbitrary_types_allowed": True}
105
+
106
+
107
+ class UCUpdateOutput(BaseModel):
108
+ """Response model for updating a UC tool."""
109
+
110
+ success: bool = Field(..., description="Whether update succeeded")
111
+ tool_name: str = Field(default="", description="Name of the updated tool")
112
+ source_path: Optional[str] = Field(
113
+ default=None, description="Path to the updated tool"
114
+ )
115
+ preview: Optional[str] = Field(
116
+ default=None, description="Preview of the first 10 lines of updated source code"
117
+ )
118
+ error: Optional[str] = Field(default=None, description="Error message if failed")
119
+ changes_applied: List[str] = Field(
120
+ default_factory=list, description="List of changes that were applied"
121
+ )
122
+
123
+ model_config = {"arbitrary_types_allowed": True}
124
+
125
+
126
+ class UCInfoOutput(BaseModel):
127
+ """Response model for getting info about a specific UC tool."""
128
+
129
+ success: bool = Field(..., description="Whether lookup succeeded")
130
+ tool: Optional[UCToolInfo] = Field(
131
+ default=None, description="Tool information if found"
132
+ )
133
+ source_code: Optional[str] = Field(
134
+ default=None, description="Source code of the tool"
135
+ )
136
+ error: Optional[str] = Field(default=None, description="Error message if failed")
137
+
138
+ model_config = {"arbitrary_types_allowed": True}