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,794 @@
1
+ """Interactive TUI for managing agent skills.
2
+
3
+ Launch with /skills to browse, enable, disable, and configure skills.
4
+ Built with prompt_toolkit for proper interactive split-panel interface.
5
+ """
6
+
7
+ import os
8
+ import sys
9
+ import time
10
+ from pathlib import Path
11
+ from typing import List, Optional
12
+
13
+ from prompt_toolkit.application import Application
14
+ from prompt_toolkit.key_binding import KeyBindings
15
+ from prompt_toolkit.layout import Dimension, Layout, VSplit, Window
16
+ from prompt_toolkit.layout.controls import FormattedTextControl
17
+ from prompt_toolkit.widgets import Frame
18
+
19
+ from code_puppy.command_line.pagination import (
20
+ ensure_visible_page,
21
+ get_page_bounds,
22
+ get_total_pages,
23
+ )
24
+ from code_puppy.command_line.utils import safe_input
25
+ from code_puppy.messaging import emit_error, emit_info, emit_success, emit_warning
26
+ from code_puppy_core_plugins.agent_skills.config import (
27
+ add_skill_directory,
28
+ get_disabled_skills,
29
+ get_skill_directories,
30
+ get_skills_enabled,
31
+ remove_skill_directory,
32
+ set_skill_disabled,
33
+ set_skills_enabled,
34
+ )
35
+ from code_puppy_core_plugins.agent_skills.discovery import (
36
+ SkillInfo,
37
+ discover_skills,
38
+ refresh_skill_cache,
39
+ )
40
+ from code_puppy_core_plugins.agent_skills.metadata import (
41
+ SkillMetadata,
42
+ get_skill_resources,
43
+ parse_skill_metadata,
44
+ )
45
+ from code_puppy.tools.command_runner import set_awaiting_user_input
46
+ from code_puppy.callbacks import on_prompt_toolkit_style
47
+
48
+ PAGE_SIZE = 15 # Items per page
49
+
50
+
51
+ class SkillsMenu:
52
+ """Interactive TUI for managing agent skills."""
53
+
54
+ def __init__(self):
55
+ """Initialize the skills menu."""
56
+ self.skills: List[SkillInfo] = []
57
+ self.disabled_skills: List[str] = []
58
+ self.skill_directories: List[Path] = []
59
+ self.skills_enabled = False
60
+
61
+ # State management
62
+ self.selected_idx = 0
63
+ self.current_page = 0
64
+ self.result = None
65
+
66
+ # UI controls (set during run)
67
+ self.menu_control: Optional[FormattedTextControl] = None
68
+ self.preview_control: Optional[FormattedTextControl] = None
69
+
70
+ # Initialize data
71
+ self._refresh_data()
72
+
73
+ def _refresh_data(self) -> None:
74
+ """Refresh skills data from disk."""
75
+ try:
76
+ self.skills = discover_skills()
77
+ self.disabled_skills = get_disabled_skills()
78
+ self.skill_directories = get_skill_directories()
79
+ self.skills_enabled = get_skills_enabled()
80
+ except Exception as e:
81
+ emit_error(f"Failed to refresh skills data: {e}")
82
+
83
+ def _get_current_skill(self) -> Optional[SkillInfo]:
84
+ """Get the currently selected skill."""
85
+ if 0 <= self.selected_idx < len(self.skills):
86
+ return self.skills[self.selected_idx]
87
+ return None
88
+
89
+ def _get_skill_metadata(self, skill: SkillInfo) -> Optional[SkillMetadata]:
90
+ """Get metadata for a skill."""
91
+ try:
92
+ return parse_skill_metadata(skill.path)
93
+ except Exception:
94
+ return None
95
+
96
+ def _is_skill_disabled(self, skill: SkillInfo) -> bool:
97
+ """Check if a skill is disabled."""
98
+ metadata = self._get_skill_metadata(skill)
99
+ if metadata:
100
+ return metadata.name in self.disabled_skills
101
+ return skill.name in self.disabled_skills
102
+
103
+ def _toggle_current_skill(self) -> None:
104
+ """Toggle the enabled/disabled state of the current skill."""
105
+ skill = self._get_current_skill()
106
+ if not skill:
107
+ return
108
+
109
+ metadata = self._get_skill_metadata(skill)
110
+ skill_name = metadata.name if metadata else skill.name
111
+
112
+ is_disabled = skill_name in self.disabled_skills
113
+ set_skill_disabled(skill_name, not is_disabled)
114
+ refresh_skill_cache()
115
+ self._refresh_data()
116
+ self.update_display()
117
+
118
+ def _render_skill_list(self) -> List:
119
+ """Render the skill list panel."""
120
+ lines = []
121
+
122
+ # Header with status
123
+ status_style = "class:tui.success" if self.skills_enabled else "class:tui.error"
124
+ status_text = "ENABLED" if self.skills_enabled else "DISABLED"
125
+ lines.append((status_style, f" Skills: {status_text}"))
126
+ lines.append(("", "\n\n"))
127
+
128
+ if not self.skills:
129
+ lines.append(("class:tui.warning", " No skills found."))
130
+ lines.append(("", "\n"))
131
+ lines.append(("class:tui.muted", " Create skills in:"))
132
+ lines.append(("", "\n"))
133
+ lines.append(("class:tui.muted", " ~/.code_puppy/skills/"))
134
+ lines.append(("", "\n"))
135
+ lines.append(("class:tui.muted", " ./skills/"))
136
+ lines.append(("", "\n\n"))
137
+ self._render_navigation_hints(lines)
138
+ return lines
139
+
140
+ # Calculate pagination
141
+ total_pages = get_total_pages(len(self.skills), PAGE_SIZE)
142
+ start_idx, end_idx = get_page_bounds(
143
+ self.current_page, len(self.skills), PAGE_SIZE
144
+ )
145
+
146
+ # Render skills
147
+ for i in range(start_idx, end_idx):
148
+ skill = self.skills[i]
149
+ is_selected = i == self.selected_idx
150
+ is_disabled = self._is_skill_disabled(skill)
151
+
152
+ # Status icon
153
+ status_icon = "✗" if is_disabled else "✓"
154
+ status_style = "class:tui.error" if is_disabled else "class:tui.success"
155
+
156
+ # Get skill name from metadata if available
157
+ metadata = self._get_skill_metadata(skill)
158
+ display_name = metadata.name if metadata else skill.name
159
+
160
+ # Format line
161
+ prefix = " > " if is_selected else " "
162
+
163
+ if is_selected:
164
+ lines.append(("class:tui.selected", prefix))
165
+ lines.append(("class:tui.selected", status_icon))
166
+ lines.append(("class:tui.selected", f" {display_name}"))
167
+ else:
168
+ lines.append(("", prefix))
169
+ lines.append((status_style, status_icon))
170
+ lines.append(("class:tui.muted", f" {display_name}"))
171
+
172
+ lines.append(("", "\n"))
173
+
174
+ # Pagination info
175
+ lines.append(("", "\n"))
176
+ lines.append(
177
+ ("class:tui.muted", f" Page {self.current_page + 1}/{total_pages}")
178
+ )
179
+ lines.append(("", "\n"))
180
+
181
+ self._render_navigation_hints(lines)
182
+ return lines
183
+
184
+ def _render_navigation_hints(self, lines: List) -> None:
185
+ """Render navigation hints at the bottom."""
186
+ lines.append(("", "\n"))
187
+ lines.append(("class:tui.help-key", " ↑/↓ or j/k "))
188
+ lines.append(("", "Navigate "))
189
+ lines.append(("class:tui.help-key", "←/→ "))
190
+ lines.append(("", "Page\n"))
191
+ lines.append(("class:tui.help-key", " Enter "))
192
+ lines.append(("", "Toggle "))
193
+ lines.append(("class:tui.help-key", " t "))
194
+ lines.append(("", "Toggle System\n"))
195
+ lines.append(("class:tui.help-key", " Ctrl+A "))
196
+ lines.append(("", "Add Dir "))
197
+ lines.append(("class:tui.help-key", " Ctrl+D "))
198
+ lines.append(("", "Show Dirs\n"))
199
+ lines.append(("class:tui.help-key", " i "))
200
+ lines.append(("", "Install from catalog\n"))
201
+ lines.append(("class:tui.help-key", " r "))
202
+ lines.append(("", "Refresh "))
203
+ lines.append(("class:tui.help-key", " q "))
204
+ lines.append(("", "Exit"))
205
+
206
+ def _render_skill_details(self) -> List:
207
+ """Render the skill details panel."""
208
+ lines = []
209
+
210
+ lines.append(("class:tui.title dim", " SKILL DETAILS"))
211
+ lines.append(("", "\n\n"))
212
+
213
+ skill = self._get_current_skill()
214
+ if not skill:
215
+ lines.append(("class:tui.warning", " No skill selected."))
216
+ lines.append(("", "\n\n"))
217
+ lines.append(("class:tui.muted", " Select a skill from the list"))
218
+ lines.append(("", "\n"))
219
+ lines.append(("class:tui.muted", " to view its details."))
220
+ return lines
221
+
222
+ metadata = self._get_skill_metadata(skill)
223
+ is_disabled = self._is_skill_disabled(skill)
224
+
225
+ # Status
226
+ status_text = "Disabled" if is_disabled else "Enabled"
227
+ status_style = "class:tui.error" if is_disabled else "class:tui.success"
228
+ lines.append(("bold", " Status: "))
229
+ lines.append((status_style, status_text))
230
+ lines.append(("", "\n\n"))
231
+
232
+ if metadata:
233
+ # Name
234
+ lines.append(("bold", f" {metadata.name}"))
235
+ lines.append(("", "\n\n"))
236
+
237
+ # Description
238
+ if metadata.description:
239
+ lines.append(("bold", " Description:"))
240
+ lines.append(("", "\n"))
241
+ # Wrap description
242
+ desc = metadata.description
243
+ wrapped = self._wrap_text(desc, 50)
244
+ for line in wrapped:
245
+ lines.append(("class:tui.muted", f" {line}"))
246
+ lines.append(("", "\n"))
247
+ lines.append(("", "\n"))
248
+
249
+ # Tags
250
+ if metadata.tags:
251
+ lines.append(("bold", " Tags:"))
252
+ lines.append(("", "\n"))
253
+ tags_str = ", ".join(metadata.tags)
254
+ lines.append(("class:tui.header", f" {tags_str}"))
255
+ lines.append(("", "\n\n"))
256
+
257
+ # Resources
258
+ resources = get_skill_resources(metadata.path)
259
+ if resources:
260
+ lines.append(("bold", " Resources:"))
261
+ lines.append(("", "\n"))
262
+ for resource in resources[:5]: # Show first 5
263
+ resource_name = getattr(resource, "name", str(resource))
264
+ lines.append(("class:tui.warning", f" • {resource_name}"))
265
+ lines.append(("", "\n"))
266
+ if len(resources) > 5:
267
+ lines.append(
268
+ ("class:tui.muted", f" ... and {len(resources) - 5} more")
269
+ )
270
+ lines.append(("", "\n"))
271
+ lines.append(("", "\n"))
272
+
273
+ else:
274
+ # No metadata available
275
+ lines.append(("bold", f" {skill.name}"))
276
+ lines.append(("", "\n\n"))
277
+ lines.append(("class:tui.warning", " No metadata available"))
278
+ lines.append(("", "\n"))
279
+ lines.append(("class:tui.muted", " Add a SKILL.md with frontmatter to"))
280
+ lines.append(("", "\n"))
281
+ lines.append(("class:tui.muted", " define name, description, and tags."))
282
+ lines.append(("", "\n\n"))
283
+
284
+ # Path
285
+ lines.append(("bold", " Path:"))
286
+ lines.append(("", "\n"))
287
+ path_str = str(skill.path)
288
+ if len(path_str) > 45:
289
+ path_str = "..." + path_str[-42:]
290
+ lines.append(("class:tui.muted", f" {path_str}"))
291
+ lines.append(("", "\n"))
292
+
293
+ return lines
294
+
295
+ def _wrap_text(self, text: str, width: int) -> List[str]:
296
+ """Wrap text to specified width."""
297
+ words = text.split()
298
+ lines = []
299
+ current_line = []
300
+ current_length = 0
301
+
302
+ for word in words:
303
+ if current_length + len(word) + 1 <= width:
304
+ current_line.append(word)
305
+ current_length += len(word) + 1
306
+ else:
307
+ if current_line:
308
+ lines.append(" ".join(current_line))
309
+ current_line = [word]
310
+ current_length = len(word)
311
+
312
+ if current_line:
313
+ lines.append(" ".join(current_line))
314
+
315
+ return lines or [""]
316
+
317
+ def update_display(self) -> None:
318
+ """Update the display based on current state."""
319
+ if self.menu_control:
320
+ self.menu_control.text = self._render_skill_list()
321
+ if self.preview_control:
322
+ self.preview_control.text = self._render_skill_details()
323
+
324
+ def run(self) -> bool:
325
+ """Run the interactive skills browser.
326
+
327
+ Returns:
328
+ True if changes were made, False otherwise.
329
+ """
330
+ # Reset per-run state
331
+ self.result = None
332
+
333
+ # Build UI
334
+ self.menu_control = FormattedTextControl(text="")
335
+ self.preview_control = FormattedTextControl(text="")
336
+
337
+ menu_window = Window(
338
+ content=self.menu_control, wrap_lines=True, width=Dimension(weight=35)
339
+ )
340
+ preview_window = Window(
341
+ content=self.preview_control, wrap_lines=True, width=Dimension(weight=65)
342
+ )
343
+
344
+ menu_frame = Frame(menu_window, width=Dimension(weight=35), title="Skills")
345
+ preview_frame = Frame(
346
+ preview_window, width=Dimension(weight=65), title="Details"
347
+ )
348
+
349
+ root_container = VSplit([menu_frame, preview_frame])
350
+
351
+ # Key bindings
352
+ kb = KeyBindings()
353
+
354
+ @kb.add("up")
355
+ @kb.add("c-p") # Ctrl+P
356
+ @kb.add("k")
357
+ def _(event):
358
+ if self.selected_idx > 0:
359
+ self.selected_idx -= 1
360
+ self.current_page = ensure_visible_page(
361
+ self.selected_idx,
362
+ self.current_page,
363
+ len(self.skills),
364
+ PAGE_SIZE,
365
+ )
366
+ self.update_display()
367
+
368
+ @kb.add("down")
369
+ @kb.add("c-n") # Ctrl+N
370
+ @kb.add("j")
371
+ def _(event):
372
+ if self.selected_idx < len(self.skills) - 1:
373
+ self.selected_idx += 1
374
+ self.current_page = ensure_visible_page(
375
+ self.selected_idx,
376
+ self.current_page,
377
+ len(self.skills),
378
+ PAGE_SIZE,
379
+ )
380
+ self.update_display()
381
+
382
+ @kb.add("left")
383
+ def _(event):
384
+ """Previous page."""
385
+ if self.current_page > 0:
386
+ self.current_page -= 1
387
+ self.selected_idx = self.current_page * PAGE_SIZE
388
+ self.update_display()
389
+
390
+ @kb.add("right")
391
+ def _(event):
392
+ """Next page."""
393
+ total_pages = get_total_pages(len(self.skills), PAGE_SIZE)
394
+ if self.current_page < total_pages - 1:
395
+ self.current_page += 1
396
+ self.selected_idx = self.current_page * PAGE_SIZE
397
+ self.update_display()
398
+
399
+ @kb.add("enter")
400
+ def _(event):
401
+ """Toggle skill enabled/disabled."""
402
+ self._toggle_current_skill()
403
+ self.result = "changed"
404
+
405
+ @kb.add("t")
406
+ def _(event):
407
+ """Toggle skills system on/off."""
408
+ new_state = not self.skills_enabled
409
+ set_skills_enabled(new_state)
410
+ self.skills_enabled = new_state
411
+ self.result = "changed"
412
+ self.update_display()
413
+
414
+ @kb.add("r")
415
+ def _(event):
416
+ """Refresh skills."""
417
+ refresh_skill_cache()
418
+ self._refresh_data()
419
+ self.update_display()
420
+
421
+ @kb.add("c-a")
422
+ def _(event):
423
+ """Add a skill directory."""
424
+ self.result = "add_directory"
425
+ event.app.exit()
426
+
427
+ @kb.add("c-d")
428
+ def _(event):
429
+ """Show/manage directories."""
430
+ self.result = "show_directories"
431
+ event.app.exit()
432
+
433
+ @kb.add("i")
434
+ def _(event):
435
+ """Install skills from catalog."""
436
+ self.result = "install"
437
+ event.app.exit()
438
+
439
+ @kb.add("q")
440
+ @kb.add("escape")
441
+ def _(event):
442
+ self.result = "quit"
443
+ event.app.exit()
444
+
445
+ @kb.add("c-c")
446
+ def _(event):
447
+ self.result = "quit"
448
+ event.app.exit()
449
+
450
+ layout = Layout(root_container)
451
+ app = Application(
452
+ layout=layout,
453
+ key_bindings=kb,
454
+ full_screen=False,
455
+ mouse_support=False,
456
+ style=on_prompt_toolkit_style(),
457
+ )
458
+
459
+ set_awaiting_user_input(True)
460
+
461
+ # Enter alternate screen buffer
462
+ sys.stdout.write("\033[?1049h") # Enter alternate buffer
463
+ sys.stdout.write("\033[2J\033[H") # Clear and home
464
+ sys.stdout.flush()
465
+ time.sleep(0.05)
466
+
467
+ try:
468
+ # Initial display
469
+ self.update_display()
470
+
471
+ # Clear the buffer
472
+ sys.stdout.write("\033[2J\033[H")
473
+ sys.stdout.flush()
474
+
475
+ # Run application in a background thread to avoid event loop conflicts
476
+ app.run(in_thread=True)
477
+
478
+ finally:
479
+ # Exit alternate screen buffer
480
+ sys.stdout.write("\033[?1049l")
481
+ sys.stdout.flush()
482
+
483
+ # Flush any buffered input to prevent stale keypresses
484
+ try:
485
+ import termios
486
+
487
+ termios.tcflush(sys.stdin.fileno(), termios.TCIFLUSH)
488
+ except Exception:
489
+ pass # ImportError on Windows, termios.error, or not a tty
490
+
491
+ # Small delay to let terminal settle before any output
492
+ time.sleep(0.1)
493
+ set_awaiting_user_input(False)
494
+
495
+ return self.result
496
+
497
+
498
+ def _prompt_for_directory() -> Optional[str]:
499
+ """Prompt user for a directory path to add."""
500
+ try:
501
+ print("\n" + "=" * 60)
502
+ print("ADD SKILL DIRECTORY")
503
+ print("=" * 60)
504
+ print("\nEnter the path to a directory containing skills.")
505
+ print("Examples:")
506
+ print(" ~/.claude/skills")
507
+ print(" /opt/shared-skills")
508
+ print(" ./my-project-skills")
509
+ print("\nPress Ctrl+C to cancel.\n")
510
+
511
+ path = safe_input("Directory path: ").strip()
512
+ if path:
513
+ # Expand ~ to home directory
514
+ expanded = os.path.expanduser(path)
515
+ return expanded
516
+ except (KeyboardInterrupt, EOFError):
517
+ print("\nCancelled.")
518
+ return None
519
+
520
+
521
+ def _show_directories_menu() -> Optional[str]:
522
+ """Show current directories and allow removal."""
523
+ try:
524
+ dirs = get_skill_directories()
525
+
526
+ print("\n" + "=" * 60)
527
+ print("SKILL DIRECTORIES")
528
+ print("=" * 60)
529
+ print("\nCurrently configured directories:\n")
530
+
531
+ if not dirs:
532
+ print(" (no directories configured)")
533
+ else:
534
+ for i, d in enumerate(dirs, 1):
535
+ exists = os.path.isdir(os.path.expanduser(d))
536
+ status = "✓" if exists else "✗ (not found)"
537
+ print(f" {i}. {d} {status}")
538
+
539
+ print("\nOptions:")
540
+ print(" Enter a number to remove that directory")
541
+ print(" Press Enter or Ctrl+C to go back\n")
542
+
543
+ choice = safe_input("Choice: ").strip()
544
+ if choice and choice.isdigit():
545
+ idx = int(choice) - 1
546
+ if 0 <= idx < len(dirs):
547
+ dir_to_remove = dirs[idx]
548
+ confirm = (
549
+ safe_input(f"Remove '{dir_to_remove}'? (y/N): ").strip().lower()
550
+ )
551
+ if confirm in ("y", "yes"):
552
+ remove_skill_directory(dir_to_remove)
553
+ print(f"Removed: {dir_to_remove}")
554
+ return "changed"
555
+ except (KeyboardInterrupt, EOFError):
556
+ print("\nCancelled.")
557
+ return None
558
+
559
+
560
+ def show_skills_menu() -> bool:
561
+ """Launch the interactive skills TUI menu.
562
+
563
+ Returns:
564
+ True if changes were made, False otherwise.
565
+ """
566
+
567
+ changes_made = False
568
+
569
+ while True:
570
+ menu = SkillsMenu()
571
+ result = menu.run()
572
+
573
+ if result == "add_directory":
574
+ # Prompt for directory to add
575
+ new_dir = _prompt_for_directory()
576
+ if new_dir:
577
+ if add_skill_directory(new_dir):
578
+ emit_success(f"Added skill directory: {new_dir}")
579
+ changes_made = True
580
+ else:
581
+ emit_warning(f"Directory already configured: {new_dir}")
582
+ # Re-run the menu
583
+ continue
584
+
585
+ elif result == "show_directories":
586
+ # Show directories management
587
+ dir_result = _show_directories_menu()
588
+ if dir_result == "changed":
589
+ changes_made = True
590
+ # Re-run the menu
591
+ continue
592
+
593
+ elif result == "install":
594
+ from code_puppy_core_plugins.agent_skills.skills_install_menu import (
595
+ run_skills_install_menu,
596
+ )
597
+
598
+ install_result = run_skills_install_menu()
599
+ if install_result:
600
+ changes_made = True
601
+ continue # Re-run the skills menu after install
602
+
603
+ elif result == "changed":
604
+ changes_made = True
605
+ break
606
+ elif result == "quit":
607
+ break
608
+ else:
609
+ # User quit or no-op
610
+ break
611
+
612
+ return changes_made
613
+
614
+
615
+ def list_skills() -> bool:
616
+ """List all discovered skills in a simple format.
617
+
618
+ Returns:
619
+ True if successful, False otherwise.
620
+ """
621
+ try:
622
+ skills = discover_skills()
623
+ disabled_skills = get_disabled_skills()
624
+
625
+ if not skills:
626
+ emit_info(
627
+ "No skills found. Create skills in ~/.code_puppy/skills/ or ./skills/"
628
+ )
629
+ return True
630
+
631
+ emit_info(f"\nFound {len(skills)} skill(s):\n")
632
+
633
+ for skill in skills:
634
+ metadata = parse_skill_metadata(skill.path)
635
+ if metadata:
636
+ is_disabled = metadata.name in disabled_skills
637
+ status = "enabled" if not is_disabled else "disabled"
638
+ emit_info(f" [{status}] {metadata.name}")
639
+ if metadata.description:
640
+ emit_info(f" {metadata.description}")
641
+ resources = get_skill_resources(metadata.path)
642
+ if resources:
643
+ emit_info(f" Resources: {len(resources)}")
644
+ else:
645
+ is_disabled = skill.name in disabled_skills
646
+ status = "enabled" if not is_disabled else "disabled"
647
+ emit_info(f" [{status}] {skill.name} (no metadata)")
648
+
649
+ return True
650
+ except Exception as e:
651
+ emit_error(f"Failed to list skills: {e}")
652
+ return False
653
+
654
+
655
+ def handle_skills_command(args: list[str]) -> bool:
656
+ """Handle skills subcommands from the CLI.
657
+
658
+ Args:
659
+ args: List of command arguments (e.g., ['enable', 'my-skill'])
660
+
661
+ Returns:
662
+ True if successful, False otherwise.
663
+ """
664
+ if not args:
665
+ # Show interactive TUI
666
+ return show_skills_menu()
667
+
668
+ command = args[0].lower()
669
+
670
+ if command == "list":
671
+ return list_skills()
672
+ elif command == "enable":
673
+ if len(args) < 2:
674
+ emit_error("Usage: /skills enable <skill-name>")
675
+ return False
676
+ return _enable_skill(args[1])
677
+ elif command == "disable":
678
+ if len(args) < 2:
679
+ emit_error("Usage: /skills disable <skill-name>")
680
+ return False
681
+ return _disable_skill(args[1])
682
+ elif command == "toggle":
683
+ return _toggle_skills_integration()
684
+ elif command == "refresh":
685
+ return _refresh_skills()
686
+ elif command == "help":
687
+ _show_help()
688
+ return True
689
+ else:
690
+ emit_error(f"Unknown command: {command}")
691
+ emit_info("Use '/skills help' to see available commands.")
692
+ return False
693
+
694
+
695
+ def _enable_skill(skill_name: str) -> bool:
696
+ """Enable a specific skill."""
697
+ try:
698
+ skills = discover_skills()
699
+ skill_names = [s.name for s in skills]
700
+
701
+ # Also check metadata names
702
+ for skill in skills:
703
+ metadata = parse_skill_metadata(skill.path)
704
+ if metadata:
705
+ skill_names.append(metadata.name)
706
+
707
+ if skill_name not in skill_names:
708
+ emit_error(f"Skill '{skill_name}' not found.")
709
+ return False
710
+
711
+ disabled = get_disabled_skills()
712
+ if skill_name not in disabled:
713
+ emit_info(f"Skill '{skill_name}' is already enabled.")
714
+ return True
715
+
716
+ set_skill_disabled(skill_name, False)
717
+ refresh_skill_cache()
718
+ emit_success(f"Skill '{skill_name}' has been enabled.")
719
+ return True
720
+ except Exception as e:
721
+ emit_error(f"Failed to enable skill '{skill_name}': {e}")
722
+ return False
723
+
724
+
725
+ def _disable_skill(skill_name: str) -> bool:
726
+ """Disable a specific skill."""
727
+ try:
728
+ skills = discover_skills()
729
+ skill_names = [s.name for s in skills]
730
+
731
+ # Also check metadata names
732
+ for skill in skills:
733
+ metadata = parse_skill_metadata(skill.path)
734
+ if metadata:
735
+ skill_names.append(metadata.name)
736
+
737
+ if skill_name not in skill_names:
738
+ emit_error(f"Skill '{skill_name}' not found.")
739
+ return False
740
+
741
+ disabled = get_disabled_skills()
742
+ if skill_name in disabled:
743
+ emit_info(f"Skill '{skill_name}' is already disabled.")
744
+ return True
745
+
746
+ set_skill_disabled(skill_name, True)
747
+ refresh_skill_cache()
748
+ emit_success(f"Skill '{skill_name}' has been disabled.")
749
+ return True
750
+ except Exception as e:
751
+ emit_error(f"Failed to disable skill '{skill_name}': {e}")
752
+ return False
753
+
754
+
755
+ def _toggle_skills_integration() -> bool:
756
+ """Toggle skills integration on/off."""
757
+ try:
758
+ current = get_skills_enabled()
759
+ new_state = not current
760
+ set_skills_enabled(new_state)
761
+
762
+ if new_state:
763
+ emit_success("Skills integration has been enabled.")
764
+ else:
765
+ emit_warning("Skills integration has been disabled.")
766
+
767
+ return True
768
+ except Exception as e:
769
+ emit_error(f"Failed to toggle skills integration: {e}")
770
+ return False
771
+
772
+
773
+ def _refresh_skills() -> bool:
774
+ """Refresh the skill cache."""
775
+ try:
776
+ emit_info("Refreshing skill cache...")
777
+ refresh_skill_cache()
778
+ emit_success("Skill cache refreshed successfully.")
779
+ return True
780
+ except Exception as e:
781
+ emit_error(f"Failed to refresh skill cache: {e}")
782
+ return False
783
+
784
+
785
+ def _show_help() -> None:
786
+ """Show help information."""
787
+ emit_info("Available commands:")
788
+ emit_info(" /skills - Show interactive TUI")
789
+ emit_info(" /skills list - List all skills")
790
+ emit_info(" /skills enable <name> - Enable a skill")
791
+ emit_info(" /skills disable <name> - Disable a skill")
792
+ emit_info(" /skills toggle - Toggle skills integration")
793
+ emit_info(" /skills refresh - Refresh skill cache")
794
+ emit_info(" /skills help - Show this help")