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,388 @@
1
+ """Plugin that adds /prune for surgical history pruning.
2
+
3
+ /prune opens a multi-select TUI of conversation history. Unlike /pop
4
+ (which slices a contiguous tail), /prune lets the user cherry-pick
5
+ arbitrary whole messages and rip them out. Selection granularity is
6
+ at the message level only — individual tool calls within a message
7
+ cannot be removed in isolation; they always travel with their parent
8
+ message (and their matching tool returns elsewhere in history are
9
+ cascaded out via the orphan-cleanup pass).
10
+
11
+ The system prompt is always preserved.
12
+
13
+ Usage:
14
+ /prune Open interactive multi-select TUI
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ from typing import Any, List, Optional, Set, Tuple
20
+
21
+ from code_puppy.callbacks import register_callback
22
+
23
+
24
+ # ── messaging wrappers ──────────────────────────────────────────────────────
25
+
26
+
27
+ def emit_error(message: Any) -> None:
28
+ from code_puppy.messaging import emit_error as _emit_error
29
+
30
+ _emit_error(message)
31
+
32
+
33
+ def emit_info(message: Any) -> None:
34
+ from code_puppy.messaging import emit_info as _emit_info
35
+
36
+ _emit_info(message)
37
+
38
+
39
+ def emit_success(message: Any) -> None:
40
+ from code_puppy.messaging import emit_success as _emit_success
41
+
42
+ _emit_success(message)
43
+
44
+
45
+ def emit_warning(message: Any) -> None:
46
+ from code_puppy.messaging import emit_warning as _emit_warning
47
+
48
+ _emit_warning(message)
49
+
50
+
51
+ # ── /help integration ───────────────────────────────────────────────────────
52
+
53
+
54
+ def _custom_help() -> List[Tuple[str, str]]:
55
+ return [
56
+ (
57
+ "prune",
58
+ "Multi-select pruner — cherry-pick whole messages to remove",
59
+ )
60
+ ]
61
+
62
+
63
+ # Tool-fragment pruning: remove unmatched tool parts after message drops.
64
+
65
+
66
+ def _collect_tool_ids(history: List[Any]) -> Tuple[Set[str], Set[str]]:
67
+ call_ids: Set[str] = set()
68
+ return_ids: Set[str] = set()
69
+ try:
70
+ from pydantic_ai.messages import ToolCallPart, ToolReturnPart
71
+ except Exception:
72
+ return call_ids, return_ids
73
+
74
+ for message in history:
75
+ for part in getattr(message, "parts", []) or []:
76
+ if isinstance(part, ToolCallPart):
77
+ tcid = getattr(part, "tool_call_id", None)
78
+ if tcid:
79
+ call_ids.add(tcid)
80
+ elif isinstance(part, ToolReturnPart):
81
+ tcid = getattr(part, "tool_call_id", None)
82
+ if tcid:
83
+ return_ids.add(tcid)
84
+ return call_ids, return_ids
85
+
86
+
87
+ def _has_orphaned_returns(message: Any, call_ids: Set[str]) -> bool:
88
+ try:
89
+ from pydantic_ai.messages import ModelRequest, ToolReturnPart
90
+
91
+ if not isinstance(message, ModelRequest):
92
+ return False
93
+ parts = getattr(message, "parts", []) or []
94
+ if not parts:
95
+ return False
96
+ if not all(isinstance(p, ToolReturnPart) for p in parts):
97
+ return False
98
+ return any(
99
+ not getattr(p, "tool_call_id", None) or p.tool_call_id not in call_ids
100
+ for p in parts
101
+ )
102
+ except Exception:
103
+ return False
104
+
105
+
106
+ def _has_orphaned_calls(message: Any, return_ids: Set[str]) -> bool:
107
+ try:
108
+ from pydantic_ai.messages import ModelResponse, ToolCallPart
109
+
110
+ if not isinstance(message, ModelResponse):
111
+ return False
112
+ for part in getattr(message, "parts", []) or []:
113
+ if isinstance(part, ToolCallPart):
114
+ tcid = getattr(part, "tool_call_id", None)
115
+ if not tcid or tcid not in return_ids:
116
+ return True
117
+ return False
118
+ except Exception:
119
+ return False
120
+
121
+
122
+ def _prune_dangling_tool_fragments(history: List[Any]) -> Tuple[List[Any], int]:
123
+ """Strip genuinely orphaned tool-call sequences from the tail."""
124
+ pruned = 0
125
+ while history:
126
+ call_ids, return_ids = _collect_tool_ids(history)
127
+ tail = history[-1]
128
+ if _has_orphaned_returns(tail, call_ids):
129
+ history.pop()
130
+ pruned += 1
131
+ continue
132
+ if _has_orphaned_calls(tail, return_ids):
133
+ history.pop()
134
+ pruned += 1
135
+ continue
136
+ break
137
+ return history, pruned
138
+
139
+
140
+ # ── core mutation ──────────────────────────────────────────────────────────
141
+
142
+
143
+ def _collect_removed_tool_call_ids(
144
+ history: List[Any],
145
+ drop_indices: Set[int],
146
+ ) -> Set[str]:
147
+ """Compute the full set of tool_call_ids whose returns must also go.
148
+
149
+ Collects every ToolCallPart id living inside the messages we're
150
+ dropping wholesale, so the matching ToolReturnPart fragments
151
+ elsewhere in history can be cascaded out as orphans.
152
+ """
153
+ removed: Set[str] = set()
154
+ try:
155
+ from pydantic_ai.messages import ModelResponse, ToolCallPart
156
+ except Exception:
157
+ return removed
158
+
159
+ for hist_idx in drop_indices:
160
+ if hist_idx < 0 or hist_idx >= len(history):
161
+ continue
162
+ msg = history[hist_idx]
163
+ if not isinstance(msg, ModelResponse):
164
+ continue
165
+ for part in getattr(msg, "parts", []) or []:
166
+ if isinstance(part, ToolCallPart):
167
+ tcid = getattr(part, "tool_call_id", None)
168
+ if tcid:
169
+ removed.add(tcid)
170
+ return removed
171
+
172
+
173
+ def _message_carries_system_prompt(message: Any) -> bool:
174
+ """True if ``message`` is a ModelRequest containing a SystemPromptPart.
175
+
176
+ Matches both a standalone system message and pydantic-ai's bundled
177
+ ``SystemPromptPart + UserPromptPart`` request.
178
+ """
179
+ try:
180
+ from pydantic_ai.messages import ModelRequest, SystemPromptPart
181
+ except Exception:
182
+ return False
183
+ if not isinstance(message, ModelRequest):
184
+ return False
185
+ for part in getattr(message, "parts", []) or []:
186
+ if isinstance(part, SystemPromptPart):
187
+ return True
188
+ return False
189
+
190
+
191
+ def _message_has_orphan_tool_return(message: Any, orphan_call_ids: Set[str]) -> bool:
192
+ """True if ``message`` is a ModelRequest carrying a ToolReturnPart
193
+ whose ``tool_call_id`` is in ``orphan_call_ids``.
194
+
195
+ Used to cascade-drop messages that would otherwise leave the model
196
+ looking at a tool result with no matching tool call (which providers
197
+ like Anthropic reject outright).
198
+ """
199
+ if not orphan_call_ids:
200
+ return False
201
+ try:
202
+ from pydantic_ai.messages import ModelRequest, ToolReturnPart
203
+
204
+ try:
205
+ from pydantic_ai.messages import RetryPromptPart # type: ignore
206
+ except Exception: # pragma: no cover — older pydantic-ai
207
+ RetryPromptPart = None # type: ignore[assignment]
208
+ except Exception:
209
+ return False
210
+ if not isinstance(message, ModelRequest):
211
+ return False
212
+ reply_kinds: tuple = (ToolReturnPart,)
213
+ if RetryPromptPart is not None:
214
+ reply_kinds = (ToolReturnPart, RetryPromptPart)
215
+ for part in getattr(message, "parts", []) or []:
216
+ if isinstance(part, reply_kinds):
217
+ tcid = getattr(part, "tool_call_id", None)
218
+ if tcid and tcid in orphan_call_ids:
219
+ return True
220
+ return False
221
+
222
+
223
+ def _perform_prune(drop_indices: Set[int]) -> None:
224
+ """Apply the prune selection to current agent history.
225
+
226
+ Operates on whole messages only: each index in ``drop_indices`` is
227
+ removed from history, any ToolReturnPart messages whose matching
228
+ ToolCallPart was dropped are cascade-removed, and orphaned
229
+ tool-call/return fragments at the tail are cleaned up.
230
+ """
231
+ from code_puppy.agents.agent_manager import get_current_agent
232
+
233
+ try:
234
+ agent = get_current_agent()
235
+ except Exception as exc:
236
+ emit_error(f"/prune: could not get current agent – {exc}")
237
+ return
238
+
239
+ history: List[Any] = list(agent.get_message_history())
240
+ if not history:
241
+ emit_warning("/prune: conversation history is empty – nothing to remove")
242
+ return
243
+
244
+ # Ignore invalid indices and SystemPromptPart messages; the system identity
245
+ # must never be pruned.
246
+ drop_indices = {
247
+ i
248
+ for i in drop_indices
249
+ if 0 <= i < len(history) and not _message_carries_system_prompt(history[i])
250
+ }
251
+
252
+ if not drop_indices:
253
+ emit_info("/prune: nothing selected – history unchanged")
254
+ return
255
+
256
+ # First pass: figure out which tool_call_ids belonged to dropped
257
+ # messages so we can cascade-drop their orphaned returns elsewhere.
258
+ orphan_call_ids = _collect_removed_tool_call_ids(history, drop_indices)
259
+
260
+ before_count = len(history)
261
+
262
+ new_history: List[Any] = []
263
+ msgs_dropped = 0
264
+ cascade_dropped = 0
265
+
266
+ for hist_idx, msg in enumerate(history):
267
+ if hist_idx in drop_indices:
268
+ msgs_dropped += 1
269
+ continue
270
+ if _message_has_orphan_tool_return(msg, orphan_call_ids):
271
+ cascade_dropped += 1
272
+ continue
273
+ new_history.append(msg)
274
+
275
+ new_history, extra_pruned = _prune_dangling_tool_fragments(new_history)
276
+ after_count = len(new_history)
277
+
278
+ try:
279
+ agent.set_message_history(new_history)
280
+ except Exception as exc:
281
+ emit_error(f"/prune: failed to update message history – {exc}")
282
+ return
283
+
284
+ summary_lines = [
285
+ ":scissors: Prune complete.",
286
+ f" · {msgs_dropped} message(s) removed by selection",
287
+ ]
288
+ if cascade_dropped:
289
+ summary_lines.append(
290
+ f" · {cascade_dropped} message(s) cascade-dropped (orphaned tool returns)"
291
+ )
292
+ if extra_pruned:
293
+ summary_lines.append(
294
+ f" · {extra_pruned} dangling tool fragment(s) cleaned from tail"
295
+ )
296
+ summary_lines.append(
297
+ f":scroll: History: {before_count - 1} → {max(after_count - 1, 0)} message(s) "
298
+ f"(excluding system prompt)"
299
+ )
300
+
301
+ emit_success("\n".join(summary_lines))
302
+
303
+ if after_count <= 1:
304
+ emit_info(":bulb: History is now empty (system prompt only). Starting fresh!")
305
+
306
+
307
+ # ── /prune dispatch ────────────────────────────────────────────────────────
308
+
309
+
310
+ def _handle_prune_command(command: str) -> bool:
311
+ del command # /prune takes no arguments anymore
312
+ _launch_menu()
313
+ return True
314
+
315
+
316
+ def _launch_menu() -> None:
317
+ from code_puppy.agents.agent_manager import get_current_agent
318
+
319
+ try:
320
+ agent = get_current_agent()
321
+ except Exception as exc:
322
+ emit_error(f"/prune: could not get current agent – {exc}")
323
+ return
324
+
325
+ raw_history: List[Any] = list(agent.get_message_history())
326
+
327
+ # Sibling modules within the same package.
328
+ from code_puppy_core_plugins.prune.prune_menu import PruneMenu
329
+ from code_puppy_core_plugins.prune.prune_model import (
330
+ ContextBudget,
331
+ annotate_context_window,
332
+ build_message_entries,
333
+ )
334
+
335
+ entries = build_message_entries(raw_history)
336
+ # Locked rows are non-prunable, so an all-locked list has nothing to toggle.
337
+ if not entries or all(e.is_locked for e in entries):
338
+ emit_info("/prune: no prunable messages")
339
+ return
340
+
341
+ # Annotate token counts and in-context flags. Failures are silent —
342
+ # the menu just won't show the indicators.
343
+ try:
344
+ budget = annotate_context_window(entries, raw_history, agent)
345
+ except Exception:
346
+ budget = ContextBudget()
347
+
348
+ try:
349
+ menu = PruneMenu(entries=entries, budget=budget)
350
+ except ValueError as exc:
351
+ emit_info(f"/prune: {exc}")
352
+ return
353
+
354
+ selection = menu.run()
355
+
356
+ if selection is None:
357
+ emit_info("/prune: cancelled")
358
+ return
359
+
360
+ if selection.is_empty:
361
+ emit_info("/prune: nothing selected – history unchanged")
362
+ return
363
+
364
+ _perform_prune(selection.history_indices_to_drop)
365
+
366
+
367
+ # ── custom_command plumbing ────────────────────────────────────────────────
368
+
369
+
370
+ def _handle_custom_command(command: str, name: str) -> Optional[bool]:
371
+ if name != "prune":
372
+ return None
373
+ return _handle_prune_command(command)
374
+
375
+
376
+ register_callback("custom_command_help", _custom_help)
377
+ register_callback("custom_command", _handle_custom_command)
378
+
379
+
380
+ __all__ = [
381
+ "_collect_removed_tool_call_ids",
382
+ "_custom_help",
383
+ "_handle_custom_command",
384
+ "_handle_prune_command",
385
+ "_message_has_orphan_tool_return",
386
+ "_perform_prune",
387
+ "_prune_dangling_tool_fragments",
388
+ ]
@@ -0,0 +1,203 @@
1
+ # Puppy Kennel
2
+
3
+ Local-first memory for Code Puppy. Inspired by [MemKennel](https://github.com/MemKennel/memkennel)'s
4
+ wings → rooms → drawers model, but backed by **SQLite + FTS5** instead of
5
+ ChromaDB. No daemon, no API key, no cloud, multi-process safe via WAL mode.
6
+
7
+ ## Why not just use MemKennel / Mem0 / Chroma?
8
+
9
+ We looked. Hard. The summary:
10
+
11
+ - **MemKennel** — Beautiful concepts, but ChromaDB's embedded `PersistentClient`
12
+ is not safe across multiple processes hitting the same kennel. Upstream issues
13
+ #1581, #948, #1646 are all flavors of "we need a daemon to make this work."
14
+ We run 20 puppies sometimes. A daemon is not in the cards.
15
+ - **Mem0** — Open issue #4892: "concurrent AsyncMemory writes corrupt Qdrant
16
+ HNSW index." Same disease. Also requires an LLM API key just to *store*
17
+ memories, and phones home to PostHog by default. No thanks.
18
+ - **ChromaDB / Qdrant / Faiss directly** — All ANN-index-backed; all multi-
19
+ process-hostile in embedded mode. ANN is only worth the trouble at >100K
20
+ vectors. We're nowhere near that.
21
+
22
+ SQLite has had production-grade full-text search (FTS5, BM25-ranked) since
23
+ 2015 and has been multi-process safe via WAL since 2010. We do not need
24
+ anything fancier.
25
+
26
+ ## Wing namespacing — compartmentalization without isolation walls
27
+
28
+ Three wing namespaces, all in one shared kennel:
29
+
30
+ | Wing | Example | Purpose |
31
+ |---|---|---|
32
+ | `repo:<path>` | `repo:/Users/mike/code/foo` | Project memory, shared across agents |
33
+ | `agent:<name>` | `agent:code-puppy` | Per-agent diary, private by convention |
34
+ | `user:default` | `user:default` | Cross-cutting user preferences |
35
+
36
+ Privacy is by convention, not encryption. If you need cryptographic
37
+ isolation for a sensitive-data agent, run that agent with
38
+ `PUPPY_KENNEL_ROOT` pointed at a private directory.
39
+
40
+ ## What it does today
41
+
42
+ **Phase 1 — passive memory:**
43
+ - **`load_prompt`** — injects a tiered, budget-aware recall block (see
44
+ [the packer](#the-packer) below) into the system prompt.
45
+ - **`agent_run_end`** — writes the agent's response to a single drawer
46
+ in the current ``repo:<cwd>`` wing.
47
+
48
+ ## Wing semantics (Phase 5)
49
+
50
+ The three wings model **who the memory is for**, not who wrote it:
51
+
52
+ | Wing | Active when... | Filled by... |
53
+ |---|---|---|
54
+ | ``user:default`` | always (cross-cutting) | explicit ``kennel_remember(wing="user")`` only |
55
+ | ``repo:<cwd>`` | cwd matches | autosave + ``kennel_remember(wing="repo")`` (default) |
56
+ | ``agent:<name>`` | selected agent matches | explicit ``kennel_remember(wing="agent")`` only |
57
+
58
+ Autosave goes **only** to the repo wing — every response is "a
59
+ conversation that happened in this project," which is genuinely
60
+ repo-scoped. The agent wing is reserved for deliberate cross-project
61
+ reflections; ``user:default`` is for facts about the human.
62
+
63
+ This kills the previous dual-write design (where each response wrote
64
+ twice, once per wing). Search-time dedup is still in place for any
65
+ legacy duplicates and for content the agent explicitly mirrors into a
66
+ second wing.
67
+
68
+ ## The packer
69
+
70
+ The recall block is assembled by `packer.py` under a configurable token
71
+ budget. Three priority classes, no LLM, no embeddings:
72
+
73
+ | Tier | Source | Quota (default) | Why |
74
+ |---|---|---|---|
75
+ | **P0** User Preferences | `user:default` wing, any role | ~30% | Short, durable, pervasive ('Mike hates emojis') |
76
+ | **P1** Project Decisions | `repo:<cwd>` wing, `role='note'` | ~30% | Sticky writes from `kennel_remember` — highest signal-to-token ratio |
77
+ | **P2** Recent Context | `repo:<cwd>` wing, `role='assistant'` | remainder | Orientation, freshness |
78
+
79
+ Drawers below `PUPPY_KENNEL_MIN_DRAWER_CHARS` (default 80) are skipped
80
+ as noise. Token estimation uses the well-known 1-token ≈ 4-chars
81
+ heuristic — accurate to ±20%, zero deps.
82
+
83
+ **Config knobs:**
84
+
85
+ | Env var | Default | Effect |
86
+ |---|---|---|
87
+ | `PUPPY_KENNEL_PROMPT_BUDGET` | `1500` | Total token budget for the block |
88
+ | `PUPPY_KENNEL_USER_PREFS_QUOTA` | `0.30` | P0 fraction |
89
+ | `PUPPY_KENNEL_STICKY_QUOTA` | `0.30` | P1 fraction |
90
+ | `PUPPY_KENNEL_MIN_DRAWER_CHARS` | `80` | Noise filter |
91
+
92
+ **Phase 2 — active tooling:**
93
+
94
+ Five agent-callable tools registered via ``register_tools``:
95
+
96
+ | Tool | Purpose |
97
+ |---|---|
98
+ | `kennel_recall(query, wing?, top_k=5, scope=?)` | BM25 search across wings, deduplicated. |
99
+ | `kennel_remember(content, wing="repo", room="notes")` | Explicit verbatim write to a chosen wing. |
100
+ | `kennel_recent(wing?, top_k=5, scope=?)` | Time-ordered drawer browsing (no query needed). |
101
+ | `kennel_list_wings()` | Discover all wings + drawer counts. |
102
+ | `kennel_stats()` | Kennel-wide totals + on-disk size. |
103
+
104
+ Wing shortcuts accepted by every tool: ``"repo"`` (current project,
105
+ default for writes), ``"agent"`` (this agent's diary), ``"user"``
106
+ (cross-cutting preferences), or an explicit wing name like
107
+ ``"team:platform"``.
108
+
109
+ Scope shortcuts for multi-wing reads: ``"default"`` (repo+agent+user),
110
+ ``"repo"``, ``"agent"``, ``"user"``, ``"all"``.
111
+
112
+ Plus a **`/kennel`** slash command suite for humans:
113
+
114
+ | Command | What it does |
115
+ |---|---|
116
+ | `/kennel` | stats + recent context preview |
117
+ | `/kennel search <q>` | BM25 search across default scope |
118
+ | `/kennel wings` | list wings + drawer counts |
119
+ | `/kennel stats` | DB size, totals, current enabled state |
120
+ | `/kennel status` | is memory currently on or off? |
121
+ | `/kennel enable` (or `on`) | turn memory on at runtime |
122
+ | `/kennel disable` (or `off`) | turn memory off at runtime (drawers preserved) |
123
+ | `/kennel help` | usage hint |
124
+
125
+ ## Enable / disable
126
+
127
+ The plugin is **disabled by default** — running in the background on
128
+ noise is off until you opt in. Flip it with the slash commands:
129
+
130
+ - ``/kennel enable`` (or ``/kennel on``) — turn memory on
131
+ - ``/kennel disable`` (or ``/kennel off``) — turn memory off
132
+
133
+ State is persisted to ``puppy.cfg`` under ``kennel_enabled`` and read on
134
+ every callback, so the toggle is live — no restart needed, and the
135
+ front end can read or write the same value.
136
+
137
+ **When disabled:**
138
+
139
+ - ``load_prompt`` returns ``None`` (no recall block in system prompt)
140
+ - ``agent_run_end`` is a no-op (nothing recorded)
141
+ - Agent-facing tools return a friendly ``error`` field explaining memory is off
142
+ - **The ``/kennel`` slash suite stays available** — ``/kennel stats``,
143
+ ``/kennel wings``, ``/kennel search`` all still work so the operator
144
+ can see what's stored, and ``/kennel enable`` is always reachable to
145
+ turn it back on.
146
+
147
+ ## Known tech debt
148
+
149
+ - **Legacy duplicates.** Kennels that pre-date Phase 5 may contain
150
+ dual-written drawers (same content in repo + agent wings).
151
+ ``search_drawers_multi`` deduplicates by content so reads are sane;
152
+ they'll age out naturally once retention/pruning ships.
153
+
154
+ ## What's still coming
155
+
156
+ - `stream_event` hook to also capture user input (not just agent output).
157
+ - Optional `fastembed` semantic re-ranking on top of FTS5 BM25.
158
+ - `/kennel prune` retention policy.
159
+
160
+ ## Environment
161
+
162
+ | Variable | Default | Effect |
163
+ |---|---|---|
164
+ | `PUPPY_KENNEL_ROOT` | `~/.code_puppy/kennel` | Where the SQLite file lives |
165
+ | `PUPPY_KENNEL_PASSIVE_LIMIT` | `5` | Drawers surfaced in passive recall |
166
+ | `PUPPY_KENNEL_MAX_DRAWER_CHARS` | `32000` | Cap on stored drawer size |
167
+
168
+ ## puppy.cfg keys
169
+
170
+ | Key | Default | Effect |
171
+ |---|---|---|
172
+ | `kennel_enabled` | unset → disabled | Single source of truth for the on/off toggle. Set to `true`/`1`/`yes`/`on` (case-insensitive) to enable. Anything else — missing, blank, or garbage — leaves the kennel off. Flipped live by `/kennel enable` and `/kennel disable`. |
173
+
174
+ ## How tools reach the agent
175
+
176
+ Code Puppy agents expose a hardcoded ``get_available_tools()`` list. To get
177
+ plugin tools onto that list without editing every agent, this plugin uses
178
+ the ``register_agent_tools`` hook — a small piece of core architecture
179
+ added alongside this plugin specifically to avoid that pattern.
180
+
181
+ ``register_tools`` defines tools and drops them into ``TOOL_REGISTRY``;
182
+ ``register_agent_tools`` says *which* tools to advertise to *which* agent.
183
+ The puppy_kennel plugin always returns all five tool names regardless of
184
+ agent — memory is universally useful. Other plugins can scope per-agent by
185
+ branching on the ``agent_name`` argument.
186
+
187
+ ## Files
188
+
189
+ ```
190
+ puppy_kennel/
191
+ ├── __init__.py
192
+ ├── config.py # paths, env vars, limits
193
+ ├── schema.py # CREATE TABLE + PRAGMA strings
194
+ ├── kennel.py # storage layer (wings, rooms, drawers)
195
+ ├── wings.py # wing-naming convention
196
+ ├── recorder.py # agent_run_end -> drawer
197
+ ├── retriever.py # load_prompt -> recall block
198
+ ├── register_callbacks.py # hook wiring (entry point)
199
+ └── README.md # you are here
200
+ ```
201
+
202
+ All files under 200 lines, every concern its own module, no SQL leaking
203
+ outside `kennel.py`. SOLID, DRY, Zen-of-Python-compatible. Cheers.
@@ -0,0 +1,6 @@
1
+ """Puppy Kennel — local-first memory for Code Puppy.
2
+
3
+ Inspired by MemKennel's wings → rooms → drawers model, but backed by
4
+ SQLite + FTS5 instead of ChromaDB. No daemon, no API key, no cloud,
5
+ multi-process safe via WAL mode.
6
+ """