newcode 0.1.1__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 (289) hide show
  1. code_puppy/__init__.py +10 -0
  2. code_puppy/__main__.py +10 -0
  3. code_puppy/agents/__init__.py +31 -0
  4. code_puppy/agents/agent_c_reviewer.py +155 -0
  5. code_puppy/agents/agent_code_puppy.py +147 -0
  6. code_puppy/agents/agent_code_reviewer.py +90 -0
  7. code_puppy/agents/agent_cpp_reviewer.py +132 -0
  8. code_puppy/agents/agent_creator_agent.py +630 -0
  9. code_puppy/agents/agent_golang_reviewer.py +151 -0
  10. code_puppy/agents/agent_helios.py +122 -0
  11. code_puppy/agents/agent_javascript_reviewer.py +160 -0
  12. code_puppy/agents/agent_manager.py +742 -0
  13. code_puppy/agents/agent_pack_leader.py +380 -0
  14. code_puppy/agents/agent_planning.py +165 -0
  15. code_puppy/agents/agent_python_programmer.py +167 -0
  16. code_puppy/agents/agent_python_reviewer.py +90 -0
  17. code_puppy/agents/agent_qa_expert.py +163 -0
  18. code_puppy/agents/agent_qa_kitten.py +208 -0
  19. code_puppy/agents/agent_scheduler.py +121 -0
  20. code_puppy/agents/agent_security_auditor.py +181 -0
  21. code_puppy/agents/agent_terminal_qa.py +323 -0
  22. code_puppy/agents/agent_typescript_reviewer.py +166 -0
  23. code_puppy/agents/base_agent.py +2145 -0
  24. code_puppy/agents/event_stream_handler.py +348 -0
  25. code_puppy/agents/json_agent.py +202 -0
  26. code_puppy/agents/pack/__init__.py +34 -0
  27. code_puppy/agents/pack/bloodhound.py +296 -0
  28. code_puppy/agents/pack/husky.py +307 -0
  29. code_puppy/agents/pack/retriever.py +380 -0
  30. code_puppy/agents/pack/shepherd.py +327 -0
  31. code_puppy/agents/pack/terrier.py +281 -0
  32. code_puppy/agents/pack/watchdog.py +357 -0
  33. code_puppy/agents/prompt_reviewer.py +145 -0
  34. code_puppy/agents/subagent_stream_handler.py +276 -0
  35. code_puppy/api/__init__.py +13 -0
  36. code_puppy/api/app.py +169 -0
  37. code_puppy/api/main.py +21 -0
  38. code_puppy/api/pty_manager.py +453 -0
  39. code_puppy/api/routers/__init__.py +12 -0
  40. code_puppy/api/routers/agents.py +36 -0
  41. code_puppy/api/routers/commands.py +217 -0
  42. code_puppy/api/routers/config.py +75 -0
  43. code_puppy/api/routers/sessions.py +234 -0
  44. code_puppy/api/templates/terminal.html +361 -0
  45. code_puppy/api/websocket.py +154 -0
  46. code_puppy/callbacks.py +674 -0
  47. code_puppy/chatgpt_codex_client.py +338 -0
  48. code_puppy/claude_cache_client.py +664 -0
  49. code_puppy/cli_runner.py +1038 -0
  50. code_puppy/command_line/__init__.py +1 -0
  51. code_puppy/command_line/add_model_menu.py +1092 -0
  52. code_puppy/command_line/agent_menu.py +662 -0
  53. code_puppy/command_line/attachments.py +395 -0
  54. code_puppy/command_line/autosave_menu.py +704 -0
  55. code_puppy/command_line/clipboard.py +527 -0
  56. code_puppy/command_line/colors_menu.py +526 -0
  57. code_puppy/command_line/command_handler.py +283 -0
  58. code_puppy/command_line/command_registry.py +150 -0
  59. code_puppy/command_line/config_commands.py +719 -0
  60. code_puppy/command_line/core_commands.py +853 -0
  61. code_puppy/command_line/diff_menu.py +865 -0
  62. code_puppy/command_line/file_path_completion.py +73 -0
  63. code_puppy/command_line/load_context_completion.py +52 -0
  64. code_puppy/command_line/mcp/__init__.py +10 -0
  65. code_puppy/command_line/mcp/base.py +32 -0
  66. code_puppy/command_line/mcp/catalog_server_installer.py +175 -0
  67. code_puppy/command_line/mcp/custom_server_form.py +688 -0
  68. code_puppy/command_line/mcp/custom_server_installer.py +195 -0
  69. code_puppy/command_line/mcp/edit_command.py +148 -0
  70. code_puppy/command_line/mcp/handler.py +138 -0
  71. code_puppy/command_line/mcp/help_command.py +147 -0
  72. code_puppy/command_line/mcp/install_command.py +214 -0
  73. code_puppy/command_line/mcp/install_menu.py +705 -0
  74. code_puppy/command_line/mcp/list_command.py +94 -0
  75. code_puppy/command_line/mcp/logs_command.py +235 -0
  76. code_puppy/command_line/mcp/remove_command.py +82 -0
  77. code_puppy/command_line/mcp/restart_command.py +100 -0
  78. code_puppy/command_line/mcp/search_command.py +123 -0
  79. code_puppy/command_line/mcp/start_all_command.py +135 -0
  80. code_puppy/command_line/mcp/start_command.py +117 -0
  81. code_puppy/command_line/mcp/status_command.py +184 -0
  82. code_puppy/command_line/mcp/stop_all_command.py +112 -0
  83. code_puppy/command_line/mcp/stop_command.py +80 -0
  84. code_puppy/command_line/mcp/test_command.py +107 -0
  85. code_puppy/command_line/mcp/utils.py +129 -0
  86. code_puppy/command_line/mcp/wizard_utils.py +334 -0
  87. code_puppy/command_line/mcp_completion.py +174 -0
  88. code_puppy/command_line/model_picker_completion.py +197 -0
  89. code_puppy/command_line/model_settings_menu.py +932 -0
  90. code_puppy/command_line/motd.py +91 -0
  91. code_puppy/command_line/onboarding_slides.py +179 -0
  92. code_puppy/command_line/onboarding_wizard.py +342 -0
  93. code_puppy/command_line/pin_command_completion.py +329 -0
  94. code_puppy/command_line/prompt_toolkit_completion.py +846 -0
  95. code_puppy/command_line/session_commands.py +302 -0
  96. code_puppy/command_line/skills_completion.py +160 -0
  97. code_puppy/command_line/uc_menu.py +893 -0
  98. code_puppy/command_line/utils.py +93 -0
  99. code_puppy/command_line/wiggum_state.py +78 -0
  100. code_puppy/config.py +1787 -0
  101. code_puppy/error_logging.py +133 -0
  102. code_puppy/gemini_code_assist.py +385 -0
  103. code_puppy/gemini_model.py +754 -0
  104. code_puppy/hook_engine/README.md +105 -0
  105. code_puppy/hook_engine/__init__.py +15 -0
  106. code_puppy/hook_engine/aliases.py +155 -0
  107. code_puppy/hook_engine/engine.py +195 -0
  108. code_puppy/hook_engine/executor.py +293 -0
  109. code_puppy/hook_engine/matcher.py +145 -0
  110. code_puppy/hook_engine/models.py +222 -0
  111. code_puppy/hook_engine/registry.py +106 -0
  112. code_puppy/hook_engine/validator.py +141 -0
  113. code_puppy/http_utils.py +361 -0
  114. code_puppy/keymap.py +128 -0
  115. code_puppy/main.py +10 -0
  116. code_puppy/mcp_/__init__.py +66 -0
  117. code_puppy/mcp_/async_lifecycle.py +286 -0
  118. code_puppy/mcp_/blocking_startup.py +469 -0
  119. code_puppy/mcp_/captured_stdio_server.py +275 -0
  120. code_puppy/mcp_/circuit_breaker.py +290 -0
  121. code_puppy/mcp_/config_wizard.py +507 -0
  122. code_puppy/mcp_/dashboard.py +308 -0
  123. code_puppy/mcp_/error_isolation.py +407 -0
  124. code_puppy/mcp_/examples/retry_example.py +226 -0
  125. code_puppy/mcp_/health_monitor.py +589 -0
  126. code_puppy/mcp_/managed_server.py +428 -0
  127. code_puppy/mcp_/manager.py +807 -0
  128. code_puppy/mcp_/mcp_logs.py +224 -0
  129. code_puppy/mcp_/registry.py +451 -0
  130. code_puppy/mcp_/retry_manager.py +337 -0
  131. code_puppy/mcp_/server_registry_catalog.py +1126 -0
  132. code_puppy/mcp_/status_tracker.py +355 -0
  133. code_puppy/mcp_/system_tools.py +209 -0
  134. code_puppy/mcp_prompts/__init__.py +1 -0
  135. code_puppy/mcp_prompts/hook_creator.py +103 -0
  136. code_puppy/messaging/__init__.py +255 -0
  137. code_puppy/messaging/bus.py +613 -0
  138. code_puppy/messaging/commands.py +167 -0
  139. code_puppy/messaging/markdown_patches.py +57 -0
  140. code_puppy/messaging/message_queue.py +361 -0
  141. code_puppy/messaging/messages.py +569 -0
  142. code_puppy/messaging/queue_console.py +271 -0
  143. code_puppy/messaging/renderers.py +311 -0
  144. code_puppy/messaging/rich_renderer.py +1153 -0
  145. code_puppy/messaging/spinner/__init__.py +83 -0
  146. code_puppy/messaging/spinner/console_spinner.py +240 -0
  147. code_puppy/messaging/spinner/spinner_base.py +96 -0
  148. code_puppy/messaging/subagent_console.py +460 -0
  149. code_puppy/model_factory.py +848 -0
  150. code_puppy/model_switching.py +63 -0
  151. code_puppy/model_utils.py +168 -0
  152. code_puppy/models.json +130 -0
  153. code_puppy/models_dev_api.json +1 -0
  154. code_puppy/models_dev_parser.py +592 -0
  155. code_puppy/plugins/__init__.py +186 -0
  156. code_puppy/plugins/agent_skills/__init__.py +22 -0
  157. code_puppy/plugins/agent_skills/config.py +175 -0
  158. code_puppy/plugins/agent_skills/discovery.py +136 -0
  159. code_puppy/plugins/agent_skills/downloader.py +392 -0
  160. code_puppy/plugins/agent_skills/installer.py +22 -0
  161. code_puppy/plugins/agent_skills/metadata.py +219 -0
  162. code_puppy/plugins/agent_skills/prompt_builder.py +100 -0
  163. code_puppy/plugins/agent_skills/register_callbacks.py +241 -0
  164. code_puppy/plugins/agent_skills/remote_catalog.py +322 -0
  165. code_puppy/plugins/agent_skills/skill_catalog.py +257 -0
  166. code_puppy/plugins/agent_skills/skills_install_menu.py +664 -0
  167. code_puppy/plugins/agent_skills/skills_menu.py +781 -0
  168. code_puppy/plugins/antigravity_oauth/__init__.py +10 -0
  169. code_puppy/plugins/antigravity_oauth/accounts.py +406 -0
  170. code_puppy/plugins/antigravity_oauth/antigravity_model.py +706 -0
  171. code_puppy/plugins/antigravity_oauth/config.py +42 -0
  172. code_puppy/plugins/antigravity_oauth/constants.py +133 -0
  173. code_puppy/plugins/antigravity_oauth/oauth.py +478 -0
  174. code_puppy/plugins/antigravity_oauth/register_callbacks.py +518 -0
  175. code_puppy/plugins/antigravity_oauth/storage.py +288 -0
  176. code_puppy/plugins/antigravity_oauth/test_plugin.py +319 -0
  177. code_puppy/plugins/antigravity_oauth/token.py +167 -0
  178. code_puppy/plugins/antigravity_oauth/transport.py +863 -0
  179. code_puppy/plugins/antigravity_oauth/utils.py +168 -0
  180. code_puppy/plugins/chatgpt_oauth/__init__.py +8 -0
  181. code_puppy/plugins/chatgpt_oauth/config.py +52 -0
  182. code_puppy/plugins/chatgpt_oauth/oauth_flow.py +328 -0
  183. code_puppy/plugins/chatgpt_oauth/register_callbacks.py +176 -0
  184. code_puppy/plugins/chatgpt_oauth/test_plugin.py +295 -0
  185. code_puppy/plugins/chatgpt_oauth/utils.py +499 -0
  186. code_puppy/plugins/claude_code_hooks/__init__.py +1 -0
  187. code_puppy/plugins/claude_code_hooks/config.py +131 -0
  188. code_puppy/plugins/claude_code_hooks/register_callbacks.py +163 -0
  189. code_puppy/plugins/claude_code_oauth/README.md +167 -0
  190. code_puppy/plugins/claude_code_oauth/SETUP.md +93 -0
  191. code_puppy/plugins/claude_code_oauth/__init__.py +25 -0
  192. code_puppy/plugins/claude_code_oauth/config.py +52 -0
  193. code_puppy/plugins/claude_code_oauth/register_callbacks.py +453 -0
  194. code_puppy/plugins/claude_code_oauth/test_plugin.py +283 -0
  195. code_puppy/plugins/claude_code_oauth/token_refresh_heartbeat.py +241 -0
  196. code_puppy/plugins/claude_code_oauth/utils.py +601 -0
  197. code_puppy/plugins/customizable_commands/__init__.py +0 -0
  198. code_puppy/plugins/customizable_commands/register_callbacks.py +152 -0
  199. code_puppy/plugins/example_custom_command/README.md +280 -0
  200. code_puppy/plugins/example_custom_command/register_callbacks.py +48 -0
  201. code_puppy/plugins/file_permission_handler/__init__.py +4 -0
  202. code_puppy/plugins/file_permission_handler/register_callbacks.py +528 -0
  203. code_puppy/plugins/frontend_emitter/__init__.py +25 -0
  204. code_puppy/plugins/frontend_emitter/emitter.py +121 -0
  205. code_puppy/plugins/frontend_emitter/register_callbacks.py +261 -0
  206. code_puppy/plugins/hook_creator/__init__.py +1 -0
  207. code_puppy/plugins/hook_creator/register_callbacks.py +33 -0
  208. code_puppy/plugins/hook_manager/__init__.py +1 -0
  209. code_puppy/plugins/hook_manager/config.py +277 -0
  210. code_puppy/plugins/hook_manager/hooks_menu.py +551 -0
  211. code_puppy/plugins/hook_manager/register_callbacks.py +205 -0
  212. code_puppy/plugins/oauth_puppy_html.py +224 -0
  213. code_puppy/plugins/scheduler/__init__.py +1 -0
  214. code_puppy/plugins/scheduler/register_callbacks.py +88 -0
  215. code_puppy/plugins/scheduler/scheduler_menu.py +522 -0
  216. code_puppy/plugins/scheduler/scheduler_wizard.py +341 -0
  217. code_puppy/plugins/shell_safety/__init__.py +6 -0
  218. code_puppy/plugins/shell_safety/agent_shell_safety.py +69 -0
  219. code_puppy/plugins/shell_safety/command_cache.py +156 -0
  220. code_puppy/plugins/shell_safety/register_callbacks.py +202 -0
  221. code_puppy/plugins/synthetic_status/__init__.py +1 -0
  222. code_puppy/plugins/synthetic_status/register_callbacks.py +132 -0
  223. code_puppy/plugins/synthetic_status/status_api.py +147 -0
  224. code_puppy/plugins/universal_constructor/__init__.py +13 -0
  225. code_puppy/plugins/universal_constructor/models.py +138 -0
  226. code_puppy/plugins/universal_constructor/register_callbacks.py +47 -0
  227. code_puppy/plugins/universal_constructor/registry.py +302 -0
  228. code_puppy/plugins/universal_constructor/sandbox.py +584 -0
  229. code_puppy/prompts/antigravity_system_prompt.md +1 -0
  230. code_puppy/pydantic_patches.py +317 -0
  231. code_puppy/reopenable_async_client.py +232 -0
  232. code_puppy/round_robin_model.py +150 -0
  233. code_puppy/scheduler/__init__.py +41 -0
  234. code_puppy/scheduler/__main__.py +9 -0
  235. code_puppy/scheduler/cli.py +118 -0
  236. code_puppy/scheduler/config.py +126 -0
  237. code_puppy/scheduler/daemon.py +280 -0
  238. code_puppy/scheduler/executor.py +155 -0
  239. code_puppy/scheduler/platform.py +19 -0
  240. code_puppy/scheduler/platform_unix.py +22 -0
  241. code_puppy/scheduler/platform_win.py +32 -0
  242. code_puppy/session_storage.py +338 -0
  243. code_puppy/status_display.py +257 -0
  244. code_puppy/summarization_agent.py +176 -0
  245. code_puppy/terminal_utils.py +418 -0
  246. code_puppy/tools/__init__.py +470 -0
  247. code_puppy/tools/agent_tools.py +616 -0
  248. code_puppy/tools/ask_user_question/__init__.py +26 -0
  249. code_puppy/tools/ask_user_question/constants.py +73 -0
  250. code_puppy/tools/ask_user_question/demo_tui.py +55 -0
  251. code_puppy/tools/ask_user_question/handler.py +232 -0
  252. code_puppy/tools/ask_user_question/models.py +304 -0
  253. code_puppy/tools/ask_user_question/registration.py +36 -0
  254. code_puppy/tools/ask_user_question/renderers.py +309 -0
  255. code_puppy/tools/ask_user_question/terminal_ui.py +329 -0
  256. code_puppy/tools/ask_user_question/theme.py +155 -0
  257. code_puppy/tools/ask_user_question/tui_loop.py +423 -0
  258. code_puppy/tools/browser/__init__.py +37 -0
  259. code_puppy/tools/browser/browser_control.py +289 -0
  260. code_puppy/tools/browser/browser_interactions.py +545 -0
  261. code_puppy/tools/browser/browser_locators.py +640 -0
  262. code_puppy/tools/browser/browser_manager.py +378 -0
  263. code_puppy/tools/browser/browser_navigation.py +251 -0
  264. code_puppy/tools/browser/browser_screenshot.py +179 -0
  265. code_puppy/tools/browser/browser_scripts.py +462 -0
  266. code_puppy/tools/browser/browser_workflows.py +221 -0
  267. code_puppy/tools/browser/chromium_terminal_manager.py +259 -0
  268. code_puppy/tools/browser/terminal_command_tools.py +534 -0
  269. code_puppy/tools/browser/terminal_screenshot_tools.py +552 -0
  270. code_puppy/tools/browser/terminal_tools.py +525 -0
  271. code_puppy/tools/command_runner.py +1346 -0
  272. code_puppy/tools/common.py +1409 -0
  273. code_puppy/tools/display.py +84 -0
  274. code_puppy/tools/file_modifications.py +739 -0
  275. code_puppy/tools/file_operations.py +802 -0
  276. code_puppy/tools/scheduler_tools.py +412 -0
  277. code_puppy/tools/skills_tools.py +251 -0
  278. code_puppy/tools/subagent_context.py +158 -0
  279. code_puppy/tools/tools_content.py +51 -0
  280. code_puppy/tools/universal_constructor.py +889 -0
  281. code_puppy/uvx_detection.py +242 -0
  282. code_puppy/version_checker.py +82 -0
  283. newcode-0.1.1.data/data/code_puppy/models.json +130 -0
  284. newcode-0.1.1.data/data/code_puppy/models_dev_api.json +1 -0
  285. newcode-0.1.1.dist-info/METADATA +154 -0
  286. newcode-0.1.1.dist-info/RECORD +289 -0
  287. newcode-0.1.1.dist-info/WHEEL +4 -0
  288. newcode-0.1.1.dist-info/entry_points.txt +3 -0
  289. newcode-0.1.1.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,205 @@
1
+ """
2
+ Hook Manager plugin – registers callbacks for interactive hook management.
3
+
4
+ Provides:
5
+ /hooks – Launch interactive TUI menu (shows global + project hooks)
6
+ /hooks list – Quick text listing of all configured hooks
7
+ /hooks enable – Enable all hooks (both global and project)
8
+ /hooks disable – Disable all hooks (both global and project)
9
+ /hooks status – Show summary counts per event type
10
+ """
11
+ import logging
12
+ from typing import Any, List, Optional, Tuple
13
+
14
+ from code_puppy.callbacks import register_callback
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+ _COMMAND_NAME = "hooks"
19
+ _ALIASES = ("hook",)
20
+
21
+
22
+ # ---------------------------------------------------------------------------
23
+ # /help entry
24
+ # ---------------------------------------------------------------------------
25
+
26
+ def _hooks_command_help() -> List[Tuple[str, str]]:
27
+ """Advertise /hooks in the /help menu."""
28
+ return [
29
+ ("hooks", "Manage Claude Code hooks (global + project) – browse, enable/disable, inspect"),
30
+ ("hook", "Alias for /hooks"),
31
+ ]
32
+
33
+
34
+ # ---------------------------------------------------------------------------
35
+ # /hooks command handler
36
+ # ---------------------------------------------------------------------------
37
+
38
+ def _handle_hooks_command(command: str, name: str) -> Optional[Any]:
39
+ """Handle /hooks (and /hook) slash commands.
40
+
41
+ Sub-commands
42
+ ------------
43
+ /hooks Launch interactive TUI menu (shows both global and project hooks)
44
+ /hooks list Print all hooks as text
45
+ /hooks enable Enable every hook (both global and project)
46
+ /hooks disable Disable every hook (both global and project)
47
+ /hooks status Show counts per event type
48
+ """
49
+ if name not in (_COMMAND_NAME, *_ALIASES):
50
+ return None # Not our command – pass through
51
+ from code_puppy.messaging import emit_error, emit_info, emit_success, emit_warning
52
+ from .config import flatten_all_hooks, _load_global_hooks_config, _load_project_hooks_config, save_global_hooks_config, save_hooks_config
53
+ import copy
54
+
55
+ tokens = command.split()
56
+ subcommand = tokens[1].lower() if len(tokens) > 1 else ""
57
+
58
+ # ------------------------------------------------------------------ list
59
+ if subcommand == "list":
60
+ entries = flatten_all_hooks()
61
+ if not entries:
62
+ emit_info("No hooks configured.")
63
+ emit_info("Add hooks to .claude/settings.json (project):")
64
+ emit_info(' { "hooks": { "PreToolUse": [ ... ] } }')
65
+ emit_info("Or to ~/.code_puppy/hooks.json (global)")
66
+ return True
67
+ emit_info(f"\U0001f3a3 Hooks ({len(entries)} total)\n")
68
+
69
+ # Group by source for clarity
70
+ project_hooks = [e for e in entries if e.source == "project"]
71
+ global_hooks = [e for e in entries if e.source == "global"]
72
+
73
+ if project_hooks:
74
+ emit_info("📁 PROJECT HOOKS (.claude/settings.json):")
75
+ for entry in project_hooks:
76
+ status = "\U0001f7e2 enabled " if entry.enabled else "\U0001f534 disabled"
77
+ emit_info(
78
+ f" {status} [{entry.event_type}] matcher={entry.display_matcher}"
79
+ )
80
+ emit_info(f" {entry.display_command}")
81
+ emit_info("")
82
+
83
+ if global_hooks:
84
+ emit_info("🌍 GLOBAL HOOKS (~/.code_puppy/hooks.json):")
85
+ for entry in global_hooks:
86
+ status = "\U0001f7e2 enabled " if entry.enabled else "\U0001f534 disabled"
87
+ emit_info(
88
+ f" {status} [{entry.event_type}] matcher={entry.display_matcher}"
89
+ )
90
+ emit_info(f" {entry.display_command}")
91
+ emit_info("")
92
+
93
+ return True
94
+ # --------------------------------------------------------------- enable
95
+ if subcommand == "enable":
96
+ count = 0
97
+
98
+ # Enable all project hooks
99
+ project_config = _load_project_hooks_config()
100
+ project_cfg = copy.deepcopy(project_config)
101
+ for groups in project_cfg.values():
102
+ if not isinstance(groups, list):
103
+ continue
104
+ for group in groups:
105
+ for hook in group.get("hooks", []):
106
+ hook["enabled"] = True
107
+ count += 1
108
+ if project_config:
109
+ save_hooks_config(project_cfg)
110
+
111
+ # Enable all global hooks
112
+ global_config = _load_global_hooks_config()
113
+ global_cfg = copy.deepcopy(global_config)
114
+ for groups in global_cfg.values():
115
+ if not isinstance(groups, list):
116
+ continue
117
+ for group in groups:
118
+ for hook in group.get("hooks", []):
119
+ hook["enabled"] = True
120
+ count += 1
121
+ if global_config:
122
+ save_global_hooks_config(global_cfg)
123
+
124
+ emit_success(f"\u2705 Enabled {count} hook(s).")
125
+ return True
126
+ # -------------------------------------------------------------- disable
127
+ if subcommand == "disable":
128
+ count = 0
129
+
130
+ # Disable all project hooks
131
+ project_config = _load_project_hooks_config()
132
+ project_cfg = copy.deepcopy(project_config)
133
+ for groups in project_cfg.values():
134
+ if not isinstance(groups, list):
135
+ continue
136
+ for group in groups:
137
+ for hook in group.get("hooks", []):
138
+ hook["enabled"] = False
139
+ count += 1
140
+ if project_config:
141
+ save_hooks_config(project_cfg)
142
+
143
+ # Disable all global hooks
144
+ global_config = _load_global_hooks_config()
145
+ global_cfg = copy.deepcopy(global_config)
146
+ for groups in global_cfg.values():
147
+ if not isinstance(groups, list):
148
+ continue
149
+ for group in groups:
150
+ for hook in group.get("hooks", []):
151
+ hook["enabled"] = False
152
+ count += 1
153
+ if global_config:
154
+ save_global_hooks_config(global_cfg)
155
+
156
+ emit_warning(f"\U0001f534 Disabled {count} hook(s).")
157
+ return True
158
+ # --------------------------------------------------------------- status
159
+ if subcommand == "status":
160
+ entries = flatten_all_hooks()
161
+ if not entries:
162
+ emit_info("No hooks configured.")
163
+ return True
164
+ from collections import Counter
165
+ by_event: Counter = Counter()
166
+ enabled_by_event: Counter = Counter()
167
+ by_source: Counter = Counter()
168
+
169
+ for entry in entries:
170
+ by_event[entry.event_type] += 1
171
+ by_source[entry.source] += 1
172
+ if entry.enabled:
173
+ enabled_by_event[entry.event_type] += 1
174
+
175
+ emit_info(
176
+ f"\U0001f4ca Hook status "
177
+ f"({sum(enabled_by_event.values())}/{len(entries)} enabled)\n"
178
+ )
179
+ for event_type, total in sorted(by_event.items()):
180
+ enabled = enabled_by_event[event_type]
181
+ bar = "\u2588" * enabled + "\u2591" * (total - enabled)
182
+ emit_info(f" {event_type:<20} {bar} {enabled}/{total}")
183
+
184
+ emit_info(f"\nSources: {by_source['project']} project, {by_source['global']} global")
185
+ emit_info("")
186
+ return True
187
+ # ----------------------------------------------- unknown sub-command
188
+ if subcommand and subcommand not in ("", "tui"):
189
+ emit_error(f"Unknown sub-command: {subcommand}")
190
+ emit_info("Usage: /hooks [list|enable|disable|status]")
191
+ return True
192
+ # --------------------------------------------------- default: TUI menu
193
+ from .hooks_menu import show_hooks_menu
194
+ show_hooks_menu()
195
+ return True
196
+
197
+
198
+ # ---------------------------------------------------------------------------
199
+ # Register callbacks
200
+ # ---------------------------------------------------------------------------
201
+
202
+ register_callback("custom_command_help", _hooks_command_help)
203
+ register_callback("custom_command", _handle_hooks_command)
204
+
205
+ logger.info("Hook Manager plugin loaded")
@@ -0,0 +1,224 @@
1
+ """Shared HTML templates for OAuth callback pages."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Optional, Tuple
6
+
7
+ CLAUDE_LOGO_URL = "https://voideditor.com/claude-icon.png"
8
+ CHATGPT_LOGO_URL = (
9
+ "https://freelogopng.com/images/all_img/1681038325chatgpt-logo-transparent.png"
10
+ )
11
+ GEMINI_LOGO_URL = "https://upload.wikimedia.org/wikipedia/commons/thumb/8/8a/Google_Gemini_logo.svg/512px-Google_Gemini_logo.svg.png"
12
+
13
+
14
+ def oauth_success_html(service_name: str, extra_message: Optional[str] = None) -> str:
15
+ """Return an animated success HTML page for OAuth completion."""
16
+ clean_service = service_name.strip() or "OAuth"
17
+ detail = f"<p class='detail'>{extra_message}</p>" if extra_message else ""
18
+ projectile, rival_url, rival_alt, target_modifier = _service_targets(clean_service)
19
+ target_classes = "target" if not target_modifier else f"target {target_modifier}"
20
+ return (
21
+ "<!DOCTYPE html>"
22
+ "<html lang='en'><head><meta charset='utf-8'>"
23
+ "<title>OAuth Success</title>"
24
+ "<style>"
25
+ "html,body{margin:0;padding:0;height:100%;overflow:hidden;font-family:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;background:linear-gradient(135deg,#0f172a 0%,#111827 45%,#1f2937 100%);color:#e5e7eb;}"
26
+ "body{display:flex;align-items:center;justify-content:center;}"
27
+ ".kennel{position:relative;width:90%;max-width:880px;padding:60px;background:rgba(15,23,42,0.72);border-radius:32px;backdrop-filter:blur(14px);box-shadow:0 30px 90px rgba(8,11,18,0.7);text-align:center;border:1px solid rgba(148,163,184,0.25);}"
28
+ "h1{font-size:3.4em;margin:0;color:#f1f5f9;text-shadow:0 14px 40px rgba(8,11,18,0.55);letter-spacing:1px;}"
29
+ "p{font-size:1.25em;margin:16px 0;color:#cbd5f5;}"
30
+ ".detail{font-size:1.1em;opacity:0.9;}"
31
+ ".mega{display:inline-block;font-size:1.35em;margin-top:14px;color:#f97316;}"
32
+ ".confetti{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:200%;height:200%;pointer-events:none;mix-blend-mode:screen;}"
33
+ ".confetti span{position:absolute;font-size:3.4em;animation:floaty 6s ease-in-out infinite;color:#fbbf24;}"
34
+ "@keyframes floaty{0%,100%{transform:translate3d(0,0,0) rotate(0deg);}35%{transform:translate3d(0,-70px,0) rotate(10deg);}65%{transform:translate3d(0,-90px,0) rotate(-12deg);}90%{transform:translate3d(0,-60px,0) rotate(6deg);}}"
35
+ ".confetti span:nth-child(odd){animation-duration:7.2s;}"
36
+ ".confetti span:nth-child(3n){animation-duration:8.6s;}"
37
+ ".confetti span:nth-child(4n){animation-duration:5.9s;}"
38
+ ".confetti span:nth-child(5n){animation-duration:9.4s;}"
39
+ ".artillery{position:absolute;bottom:12%;left:0;width:100%;max-width:1100px;height:240px;pointer-events:none;overflow:visible;}"
40
+ ".artillery .cannon{position:absolute;bottom:0;font-size:3.4em;color:#f97316;filter:drop-shadow(0 12px 32px rgba(249,115,22,0.45));}"
41
+ ".artillery .cannon.left{left:4%;}"
42
+ ".artillery .cannon.right{left:12%;transform:rotate(-4deg);}"
43
+ ".artillery .shell{position:absolute;left:10%;font-size:2.6em;animation:strafe 2.6s ease-out infinite;color:#facc15;text-shadow:0 0 14px rgba(250,204,21,0.45);}"
44
+ "@keyframes strafe{0%{left:10%;opacity:1;}60%{left:72%;opacity:1;}100%{left:82%;opacity:0;}}"
45
+ ".target{position:absolute;top:175px;right:-10%;width:220px;filter:drop-shadow(0 24px 46px rgba(8,11,18,0.72));animation:targetShake 1.9s ease-in-out infinite;}"
46
+ ".target img{width:200px;height:auto;border-radius:18px;background:#0f172a;padding:16px;border:1px solid rgba(148,163,184,0.35);}"
47
+ ".target.invert img{filter:brightness(1.2) saturate(1.15);background:rgba(15,23,42,0.9);}"
48
+ "@keyframes targetShake{0%,100%{transform:rotate(0deg) scale(1);}30%{transform:rotate(-4deg) scale(1.05);}60%{transform:rotate(3deg) scale(0.97);}85%{transform:rotate(-2deg) scale(1.04);}}"
49
+ ".target::after{content:'';position:absolute;top:50%;left:50%;width:220px;height:220px;border-radius:50%;background:radial-gradient(circle,rgba(248,113,113,0.35)0%,rgba(248,113,113,0)70%);transform:translate(-50%,-50%) scale(0);animation:impact 2.6s ease-out infinite;opacity:0;mix-blend-mode:screen;}"
50
+ "@keyframes impact{0%,60%{transform:translate(-50%,-50%) scale(0);opacity:0;}70%{transform:translate(-50%,-50%) scale(1.2);opacity:1;}100%{transform:translate(-50%,-50%) scale(1.5);opacity:0;}}"
51
+ "</style>"
52
+ "</head><body>"
53
+ "<div class='kennel'>"
54
+ "<div class='confetti'>"
55
+ + "".join(
56
+ f"<span style='left:{left}%;top:{top}%;animation-delay:{delay}s;'>{emoji}</span>"
57
+ for left, top, delay, emoji in _SUCCESS_EMOJIS
58
+ )
59
+ + "</div>"
60
+ f"<h1>&#x2705; {clean_service} OAuth Complete</h1>"
61
+ "<p class='mega'>Authentication token received successfully.</p>"
62
+ f"{detail}"
63
+ "<p>This window will close automatically.</p>"
64
+ f"<div class='{target_classes}'><img src='{rival_url}' alt='{rival_alt}'></div>"
65
+ "<div class='artillery'>" + _build_artillery(projectile) + "</div>"
66
+ "</div>"
67
+ "<script>setTimeout(()=>window.close(),3500);</script>"
68
+ "</body></html>"
69
+ )
70
+
71
+
72
+ def oauth_failure_html(service_name: str, reason: str) -> str:
73
+ """Return an error HTML page for OAuth failure."""
74
+ clean_service = service_name.strip() or "OAuth"
75
+ clean_reason = reason.strip() or "An unexpected error occurred"
76
+ projectile, rival_url, rival_alt, target_modifier = _service_targets(clean_service)
77
+ target_classes = "target" if not target_modifier else f"target {target_modifier}"
78
+ return (
79
+ "<!DOCTYPE html>"
80
+ "<html lang='en'><head><meta charset='utf-8'>"
81
+ "<title>OAuth Error</title>"
82
+ "<style>"
83
+ "html,body{margin:0;padding:0;height:100%;overflow:hidden;font-family:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;background:linear-gradient(160deg,#101827 0%,#0b1120 100%);color:#e2e8f0;}"
84
+ "body{display:flex;align-items:center;justify-content:center;}"
85
+ ".kennel{position:relative;width:90%;max-width:780px;padding:55px;background:rgba(10,13,23,0.78);border-radius:30px;box-shadow:0 26px 80px rgba(2,6,23,0.78);text-align:center;border:1px solid rgba(71,85,105,0.35);}"
86
+ "h1{font-size:3em;margin:0 0 14px;text-shadow:0 16px 36px rgba(15,23,42,0.7);color:#f87171;}"
87
+ "p{font-size:1.2em;margin:14px 0;line-height:1.6;color:#cbd5f5;}"
88
+ ".howl{font-size:1.35em;margin:18px 0;color:#fda4af;}"
89
+ ".tearstorm{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:190%;height:190%;pointer-events:none;mix-blend-mode:screen;}"
90
+ ".tearstorm span{position:absolute;font-size:3.2em;animation:weep 4.8s ease-in-out infinite;color:#60a5fa;}"
91
+ "@keyframes weep{0%{transform:translate3d(0,-10px,0) rotate(-6deg);opacity:0.85;}35%{transform:translate3d(-20px,18px,0) rotate(8deg);opacity:1;}65%{transform:translate3d(25px,28px,0) rotate(-12deg);opacity:0.8;}100%{transform:translate3d(0,60px,0) rotate(0deg);opacity:0;}}"
92
+ ".tearstorm span:nth-child(odd){animation-duration:5.8s;}"
93
+ ".tearstorm span:nth-child(3n){animation-duration:6.4s;}"
94
+ ".tearstorm span:nth-child(4n){animation-duration:7.3s;}"
95
+ ".buttons{margin-top:26px;}"
96
+ ".buttons a{display:inline-block;margin:6px 12px;padding:12px 28px;border-radius:999px;background:rgba(59,130,246,0.16);color:#bfdbfe;text-decoration:none;font-weight:600;border:1px solid rgba(96,165,250,0.4);transition:all 0.3s;}"
97
+ ".buttons a:hover{background:rgba(96,165,250,0.28);transform:translateY(-2px);}"
98
+ ".battlefield{position:absolute;bottom:-25px;left:0;width:100%;max-width:960px;height:220px;pointer-events:none;}"
99
+ ".battlefield .shell{position:absolute;left:10%;font-size:2.4em;color:#38bdf8;text-shadow:0 0 12px rgba(56,189,248,0.45);animation:strafeSad 3s ease-out infinite;}"
100
+ "@keyframes strafeSad{0%{left:10%;opacity:1;}65%{left:70%;opacity:1;}100%{left:80%;opacity:0;}}"
101
+ ".battlefield .target{position:absolute;top:16px;right:6%;width:220px;filter:drop-shadow(0 20px 44px rgba(2,6,23,0.78));animation:sway 2s ease-in-out infinite;}"
102
+ ".battlefield .target img{width:200px;height:auto;border-radius:18px;background:#0b1120;padding:16px;border:1px solid rgba(96,165,250,0.4);}"
103
+ ".battlefield .target.invert img{filter:brightness(1.2) saturate(1.15);background:rgba(15,23,42,0.9);}"
104
+ "@keyframes sway{0%,100%{transform:rotate(0deg);}40%{transform:rotate(-6deg);}70%{transform:rotate(5deg);}}"
105
+ "</style>"
106
+ "</head><body>"
107
+ "<div class='kennel'>"
108
+ "<div class='tearstorm'>"
109
+ + "".join(
110
+ f"<span style='left:{left}%;top:{top}%;animation-delay:{delay}s;'>{emoji}</span>"
111
+ for left, top, delay, emoji in _FAILURE_EMOJIS
112
+ )
113
+ + "</div>"
114
+ f"<h1>&#x274C; {clean_service} OAuth Failed</h1>"
115
+ "<p class='howl'>Authentication could not be completed.</p>"
116
+ f"<p>{clean_reason}</p>"
117
+ "<p>Please try again from the application.</p>"
118
+ "<div class='buttons'>"
119
+ "<a href='https://github.com/janfeddersen-wq/new_code' target='_blank'>Documentation</a>"
120
+ "</div>"
121
+ "<div class='battlefield'>"
122
+ + _build_artillery(projectile, shells_only=True)
123
+ + f"<div class='{target_classes}'><img src='{rival_url}' alt='{rival_alt}'></div>"
124
+ + "</div>"
125
+ "</div>"
126
+ "</body></html>"
127
+ )
128
+
129
+
130
+ _SUCCESS_EMOJIS = (
131
+ (5, 12, 0.0, "&#x2728;"),
132
+ (18, 28, 0.2, "&#x2705;"),
133
+ (32, 6, 1.1, "&#x1F389;"),
134
+ (46, 18, 0.5, "&#x2728;"),
135
+ (62, 9, 0.8, "&#x1F4AB;"),
136
+ (76, 22, 1.3, "&#x2705;"),
137
+ (88, 14, 0.4, "&#x2728;"),
138
+ (12, 48, 0.6, "&#x1F389;"),
139
+ (28, 58, 1.7, "&#x1F4AB;"),
140
+ (44, 42, 0.9, "&#x2728;"),
141
+ (58, 52, 1.5, "&#x2705;"),
142
+ (72, 46, 0.3, "&#x1F389;"),
143
+ (86, 54, 1.1, "&#x1F4AB;"),
144
+ (8, 72, 0.7, "&#x2728;"),
145
+ (24, 80, 1.2, "&#x1F389;"),
146
+ (40, 74, 0.2, "&#x2705;"),
147
+ (56, 66, 1.6, "&#x2728;"),
148
+ (70, 78, 1.0, "&#x1F4AB;"),
149
+ (84, 70, 1.4, "&#x2705;"),
150
+ (16, 90, 0.5, "&#x2728;"),
151
+ (32, 92, 1.9, "&#x1F4AB;"),
152
+ (48, 88, 1.1, "&#x1F389;"),
153
+ (64, 94, 1.8, "&#x2728;"),
154
+ (78, 88, 0.6, "&#x2705;"),
155
+ (90, 82, 1.3, "&#x1F4AB;"),
156
+ )
157
+
158
+
159
+ _FAILURE_EMOJIS = (
160
+ (8, 6, 0.0, "&#x26A0;"),
161
+ (22, 18, 0.3, "&#x274C;"),
162
+ (36, 10, 0.6, "&#x26A0;"),
163
+ (50, 20, 0.9, "&#x274C;"),
164
+ (64, 8, 1.2, "&#x26A0;"),
165
+ (78, 16, 1.5, "&#x274C;"),
166
+ (12, 38, 0.4, "&#x26A0;"),
167
+ (28, 44, 0.7, "&#x274C;"),
168
+ (42, 34, 1.0, "&#x26A0;"),
169
+ (58, 46, 1.3, "&#x274C;"),
170
+ (72, 36, 1.6, "&#x26A0;"),
171
+ (86, 40, 1.9, "&#x274C;"),
172
+ (16, 64, 0.5, "&#x26A0;"),
173
+ (32, 70, 0.8, "&#x274C;"),
174
+ (48, 60, 1.1, "&#x26A0;"),
175
+ (62, 74, 1.4, "&#x274C;"),
176
+ (78, 68, 1.7, "&#x26A0;"),
177
+ (90, 72, 2.0, "&#x274C;"),
178
+ (20, 88, 0.6, "&#x26A0;"),
179
+ (36, 92, 0.9, "&#x274C;"),
180
+ (52, 86, 1.2, "&#x26A0;"),
181
+ (68, 94, 1.5, "&#x274C;"),
182
+ (82, 90, 1.8, "&#x26A0;"),
183
+ )
184
+
185
+
186
+ _STRAFE_SHELLS: Tuple[Tuple[float, float], ...] = (
187
+ (22.0, 0.0),
188
+ (28.0, 0.35),
189
+ (34.0, 0.7),
190
+ (26.0, 0.2),
191
+ (32.0, 0.55),
192
+ (24.0, 0.9),
193
+ (30.0, 1.25),
194
+ )
195
+
196
+
197
+ def _build_artillery(projectile: str, *, shells_only: bool = False) -> str:
198
+ """Return HTML spans for animated shells (and cannons when desired)."""
199
+ shell_markup = []
200
+ for index, (top, delay) in enumerate(_STRAFE_SHELLS):
201
+ duration = 2.3 + (index % 3) * 0.25
202
+ shell_markup.append(
203
+ f"<span class='shell' style='top:{top}%;animation-delay:-{delay}s;animation-duration:{duration}s;'>{projectile}</span>"
204
+ )
205
+ shells = "".join(shell_markup)
206
+ if shells_only:
207
+ return shells
208
+
209
+ cannons = (
210
+ "<span class='cannon left'>&#x1F680;</span><span class='cannon right'>&#x26A1;</span>"
211
+ )
212
+ return cannons + shells
213
+
214
+
215
+ def _service_targets(service_name: str) -> Tuple[str, str, str, str]:
216
+ """Map service names to projectile emoji and rival logo metadata."""
217
+ normalized = service_name.lower()
218
+ if "anthropic" in normalized or "claude" in normalized:
219
+ return "&#x26A1;", CLAUDE_LOGO_URL, "Claude logo", ""
220
+ if "chat" in normalized or "gpt" in normalized:
221
+ return "&#x1F680;", CHATGPT_LOGO_URL, "ChatGPT logo", "invert"
222
+ if "gemini" in normalized or "google" in normalized:
223
+ return "&#x2728;", GEMINI_LOGO_URL, "Gemini logo", ""
224
+ return "&#x1F4AB;", CHATGPT_LOGO_URL, "mystery logo", "invert"
@@ -0,0 +1 @@
1
+ """Scheduler plugin - manages scheduled tasks via /scheduler command."""
@@ -0,0 +1,88 @@
1
+ """Scheduler plugin callbacks.
2
+
3
+ Registers the /scheduler command (and aliases /sched, /cron) via the
4
+ custom_command and custom_command_help callback hooks, keeping all
5
+ scheduler UI out of the core application.
6
+ """
7
+
8
+ from typing import Any, List, Optional, Tuple
9
+
10
+ from code_puppy.callbacks import register_callback
11
+ from code_puppy.messaging import emit_error, emit_info
12
+
13
+ _COMMAND_NAME = "scheduler"
14
+ _ALIASES = ("sched", "cron")
15
+ _DESCRIPTION = "Manage scheduled tasks – create, run, and monitor automated prompts"
16
+
17
+
18
+ def _scheduler_help() -> List[Tuple[str, str]]:
19
+ """Return help entries for the scheduler commands."""
20
+ return [
21
+ (
22
+ "scheduler",
23
+ "Manage scheduled tasks – launch TUI or use sub-commands",
24
+ ),
25
+ ("sched", "Alias for /scheduler"),
26
+ ("cron", "Alias for /scheduler"),
27
+ ]
28
+
29
+
30
+ def _handle_scheduler_command(command: str, name: str) -> Optional[Any]:
31
+ """Handle /scheduler, /sched, and /cron slash commands.
32
+
33
+ Sub-commands:
34
+ /scheduler – Launch interactive TUI menu
35
+ /scheduler start – Start the scheduler daemon
36
+ /scheduler stop – Stop the scheduler daemon
37
+ /scheduler status – Show daemon status
38
+ /scheduler list – List all tasks
39
+ /scheduler run <id>– Run a task immediately
40
+ """
41
+ if name not in (_COMMAND_NAME, *_ALIASES):
42
+ return None
43
+
44
+ from code_puppy.plugins.scheduler.scheduler_menu import show_scheduler_menu
45
+ from code_puppy.scheduler.cli import (
46
+ handle_scheduler_list,
47
+ handle_scheduler_run,
48
+ handle_scheduler_start,
49
+ handle_scheduler_status,
50
+ handle_scheduler_stop,
51
+ )
52
+
53
+ tokens = command.split()
54
+
55
+ # Handle sub-commands
56
+ if len(tokens) > 1:
57
+ subcommand = tokens[1].lower()
58
+
59
+ if subcommand == "start":
60
+ handle_scheduler_start()
61
+ return True
62
+ elif subcommand == "stop":
63
+ handle_scheduler_stop()
64
+ return True
65
+ elif subcommand == "status":
66
+ handle_scheduler_status()
67
+ return True
68
+ elif subcommand == "list":
69
+ handle_scheduler_list()
70
+ return True
71
+ elif subcommand == "run":
72
+ if len(tokens) < 3:
73
+ emit_error("Usage: /scheduler run <task_id>")
74
+ return True
75
+ handle_scheduler_run(tokens[2])
76
+ return True
77
+ else:
78
+ emit_error(f"Unknown subcommand: {subcommand}")
79
+ emit_info("Usage: /scheduler [start|stop|status|list|run <id>]")
80
+ return True
81
+
82
+ # No subcommand – launch TUI menu
83
+ show_scheduler_menu()
84
+ return True
85
+
86
+
87
+ register_callback("custom_command_help", _scheduler_help)
88
+ register_callback("custom_command", _handle_scheduler_command)