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.
- code_puppy/__init__.py +10 -0
- code_puppy/__main__.py +10 -0
- code_puppy/agents/__init__.py +31 -0
- code_puppy/agents/agent_c_reviewer.py +155 -0
- code_puppy/agents/agent_code_puppy.py +147 -0
- code_puppy/agents/agent_code_reviewer.py +90 -0
- code_puppy/agents/agent_cpp_reviewer.py +132 -0
- code_puppy/agents/agent_creator_agent.py +630 -0
- code_puppy/agents/agent_golang_reviewer.py +151 -0
- code_puppy/agents/agent_helios.py +122 -0
- code_puppy/agents/agent_javascript_reviewer.py +160 -0
- code_puppy/agents/agent_manager.py +742 -0
- code_puppy/agents/agent_pack_leader.py +380 -0
- code_puppy/agents/agent_planning.py +165 -0
- code_puppy/agents/agent_python_programmer.py +167 -0
- code_puppy/agents/agent_python_reviewer.py +90 -0
- code_puppy/agents/agent_qa_expert.py +163 -0
- code_puppy/agents/agent_qa_kitten.py +208 -0
- code_puppy/agents/agent_scheduler.py +121 -0
- code_puppy/agents/agent_security_auditor.py +181 -0
- code_puppy/agents/agent_terminal_qa.py +323 -0
- code_puppy/agents/agent_typescript_reviewer.py +166 -0
- code_puppy/agents/base_agent.py +2145 -0
- code_puppy/agents/event_stream_handler.py +348 -0
- code_puppy/agents/json_agent.py +202 -0
- code_puppy/agents/pack/__init__.py +34 -0
- code_puppy/agents/pack/bloodhound.py +296 -0
- code_puppy/agents/pack/husky.py +307 -0
- code_puppy/agents/pack/retriever.py +380 -0
- code_puppy/agents/pack/shepherd.py +327 -0
- code_puppy/agents/pack/terrier.py +281 -0
- code_puppy/agents/pack/watchdog.py +357 -0
- code_puppy/agents/prompt_reviewer.py +145 -0
- code_puppy/agents/subagent_stream_handler.py +276 -0
- code_puppy/api/__init__.py +13 -0
- code_puppy/api/app.py +169 -0
- code_puppy/api/main.py +21 -0
- code_puppy/api/pty_manager.py +453 -0
- code_puppy/api/routers/__init__.py +12 -0
- code_puppy/api/routers/agents.py +36 -0
- code_puppy/api/routers/commands.py +217 -0
- code_puppy/api/routers/config.py +75 -0
- code_puppy/api/routers/sessions.py +234 -0
- code_puppy/api/templates/terminal.html +361 -0
- code_puppy/api/websocket.py +154 -0
- code_puppy/callbacks.py +674 -0
- code_puppy/chatgpt_codex_client.py +338 -0
- code_puppy/claude_cache_client.py +664 -0
- code_puppy/cli_runner.py +1038 -0
- code_puppy/command_line/__init__.py +1 -0
- code_puppy/command_line/add_model_menu.py +1092 -0
- code_puppy/command_line/agent_menu.py +662 -0
- code_puppy/command_line/attachments.py +395 -0
- code_puppy/command_line/autosave_menu.py +704 -0
- code_puppy/command_line/clipboard.py +527 -0
- code_puppy/command_line/colors_menu.py +526 -0
- code_puppy/command_line/command_handler.py +283 -0
- code_puppy/command_line/command_registry.py +150 -0
- code_puppy/command_line/config_commands.py +719 -0
- code_puppy/command_line/core_commands.py +853 -0
- code_puppy/command_line/diff_menu.py +865 -0
- code_puppy/command_line/file_path_completion.py +73 -0
- code_puppy/command_line/load_context_completion.py +52 -0
- code_puppy/command_line/mcp/__init__.py +10 -0
- code_puppy/command_line/mcp/base.py +32 -0
- code_puppy/command_line/mcp/catalog_server_installer.py +175 -0
- code_puppy/command_line/mcp/custom_server_form.py +688 -0
- code_puppy/command_line/mcp/custom_server_installer.py +195 -0
- code_puppy/command_line/mcp/edit_command.py +148 -0
- code_puppy/command_line/mcp/handler.py +138 -0
- code_puppy/command_line/mcp/help_command.py +147 -0
- code_puppy/command_line/mcp/install_command.py +214 -0
- code_puppy/command_line/mcp/install_menu.py +705 -0
- code_puppy/command_line/mcp/list_command.py +94 -0
- code_puppy/command_line/mcp/logs_command.py +235 -0
- code_puppy/command_line/mcp/remove_command.py +82 -0
- code_puppy/command_line/mcp/restart_command.py +100 -0
- code_puppy/command_line/mcp/search_command.py +123 -0
- code_puppy/command_line/mcp/start_all_command.py +135 -0
- code_puppy/command_line/mcp/start_command.py +117 -0
- code_puppy/command_line/mcp/status_command.py +184 -0
- code_puppy/command_line/mcp/stop_all_command.py +112 -0
- code_puppy/command_line/mcp/stop_command.py +80 -0
- code_puppy/command_line/mcp/test_command.py +107 -0
- code_puppy/command_line/mcp/utils.py +129 -0
- code_puppy/command_line/mcp/wizard_utils.py +334 -0
- code_puppy/command_line/mcp_completion.py +174 -0
- code_puppy/command_line/model_picker_completion.py +197 -0
- code_puppy/command_line/model_settings_menu.py +932 -0
- code_puppy/command_line/motd.py +91 -0
- code_puppy/command_line/onboarding_slides.py +179 -0
- code_puppy/command_line/onboarding_wizard.py +342 -0
- code_puppy/command_line/pin_command_completion.py +329 -0
- code_puppy/command_line/prompt_toolkit_completion.py +846 -0
- code_puppy/command_line/session_commands.py +302 -0
- code_puppy/command_line/skills_completion.py +160 -0
- code_puppy/command_line/uc_menu.py +893 -0
- code_puppy/command_line/utils.py +93 -0
- code_puppy/command_line/wiggum_state.py +78 -0
- code_puppy/config.py +1787 -0
- code_puppy/error_logging.py +133 -0
- code_puppy/gemini_code_assist.py +385 -0
- code_puppy/gemini_model.py +754 -0
- code_puppy/hook_engine/README.md +105 -0
- code_puppy/hook_engine/__init__.py +15 -0
- code_puppy/hook_engine/aliases.py +155 -0
- code_puppy/hook_engine/engine.py +195 -0
- code_puppy/hook_engine/executor.py +293 -0
- code_puppy/hook_engine/matcher.py +145 -0
- code_puppy/hook_engine/models.py +222 -0
- code_puppy/hook_engine/registry.py +106 -0
- code_puppy/hook_engine/validator.py +141 -0
- code_puppy/http_utils.py +361 -0
- code_puppy/keymap.py +128 -0
- code_puppy/main.py +10 -0
- code_puppy/mcp_/__init__.py +66 -0
- code_puppy/mcp_/async_lifecycle.py +286 -0
- code_puppy/mcp_/blocking_startup.py +469 -0
- code_puppy/mcp_/captured_stdio_server.py +275 -0
- code_puppy/mcp_/circuit_breaker.py +290 -0
- code_puppy/mcp_/config_wizard.py +507 -0
- code_puppy/mcp_/dashboard.py +308 -0
- code_puppy/mcp_/error_isolation.py +407 -0
- code_puppy/mcp_/examples/retry_example.py +226 -0
- code_puppy/mcp_/health_monitor.py +589 -0
- code_puppy/mcp_/managed_server.py +428 -0
- code_puppy/mcp_/manager.py +807 -0
- code_puppy/mcp_/mcp_logs.py +224 -0
- code_puppy/mcp_/registry.py +451 -0
- code_puppy/mcp_/retry_manager.py +337 -0
- code_puppy/mcp_/server_registry_catalog.py +1126 -0
- code_puppy/mcp_/status_tracker.py +355 -0
- code_puppy/mcp_/system_tools.py +209 -0
- code_puppy/mcp_prompts/__init__.py +1 -0
- code_puppy/mcp_prompts/hook_creator.py +103 -0
- code_puppy/messaging/__init__.py +255 -0
- code_puppy/messaging/bus.py +613 -0
- code_puppy/messaging/commands.py +167 -0
- code_puppy/messaging/markdown_patches.py +57 -0
- code_puppy/messaging/message_queue.py +361 -0
- code_puppy/messaging/messages.py +569 -0
- code_puppy/messaging/queue_console.py +271 -0
- code_puppy/messaging/renderers.py +311 -0
- code_puppy/messaging/rich_renderer.py +1153 -0
- code_puppy/messaging/spinner/__init__.py +83 -0
- code_puppy/messaging/spinner/console_spinner.py +240 -0
- code_puppy/messaging/spinner/spinner_base.py +96 -0
- code_puppy/messaging/subagent_console.py +460 -0
- code_puppy/model_factory.py +848 -0
- code_puppy/model_switching.py +63 -0
- code_puppy/model_utils.py +168 -0
- code_puppy/models.json +130 -0
- code_puppy/models_dev_api.json +1 -0
- code_puppy/models_dev_parser.py +592 -0
- code_puppy/plugins/__init__.py +186 -0
- code_puppy/plugins/agent_skills/__init__.py +22 -0
- code_puppy/plugins/agent_skills/config.py +175 -0
- code_puppy/plugins/agent_skills/discovery.py +136 -0
- code_puppy/plugins/agent_skills/downloader.py +392 -0
- code_puppy/plugins/agent_skills/installer.py +22 -0
- code_puppy/plugins/agent_skills/metadata.py +219 -0
- code_puppy/plugins/agent_skills/prompt_builder.py +100 -0
- code_puppy/plugins/agent_skills/register_callbacks.py +241 -0
- code_puppy/plugins/agent_skills/remote_catalog.py +322 -0
- code_puppy/plugins/agent_skills/skill_catalog.py +257 -0
- code_puppy/plugins/agent_skills/skills_install_menu.py +664 -0
- code_puppy/plugins/agent_skills/skills_menu.py +781 -0
- code_puppy/plugins/antigravity_oauth/__init__.py +10 -0
- code_puppy/plugins/antigravity_oauth/accounts.py +406 -0
- code_puppy/plugins/antigravity_oauth/antigravity_model.py +706 -0
- code_puppy/plugins/antigravity_oauth/config.py +42 -0
- code_puppy/plugins/antigravity_oauth/constants.py +133 -0
- code_puppy/plugins/antigravity_oauth/oauth.py +478 -0
- code_puppy/plugins/antigravity_oauth/register_callbacks.py +518 -0
- code_puppy/plugins/antigravity_oauth/storage.py +288 -0
- code_puppy/plugins/antigravity_oauth/test_plugin.py +319 -0
- code_puppy/plugins/antigravity_oauth/token.py +167 -0
- code_puppy/plugins/antigravity_oauth/transport.py +863 -0
- code_puppy/plugins/antigravity_oauth/utils.py +168 -0
- code_puppy/plugins/chatgpt_oauth/__init__.py +8 -0
- code_puppy/plugins/chatgpt_oauth/config.py +52 -0
- code_puppy/plugins/chatgpt_oauth/oauth_flow.py +328 -0
- code_puppy/plugins/chatgpt_oauth/register_callbacks.py +176 -0
- code_puppy/plugins/chatgpt_oauth/test_plugin.py +295 -0
- code_puppy/plugins/chatgpt_oauth/utils.py +499 -0
- code_puppy/plugins/claude_code_hooks/__init__.py +1 -0
- code_puppy/plugins/claude_code_hooks/config.py +131 -0
- code_puppy/plugins/claude_code_hooks/register_callbacks.py +163 -0
- code_puppy/plugins/claude_code_oauth/README.md +167 -0
- code_puppy/plugins/claude_code_oauth/SETUP.md +93 -0
- code_puppy/plugins/claude_code_oauth/__init__.py +25 -0
- code_puppy/plugins/claude_code_oauth/config.py +52 -0
- code_puppy/plugins/claude_code_oauth/register_callbacks.py +453 -0
- code_puppy/plugins/claude_code_oauth/test_plugin.py +283 -0
- code_puppy/plugins/claude_code_oauth/token_refresh_heartbeat.py +241 -0
- code_puppy/plugins/claude_code_oauth/utils.py +601 -0
- code_puppy/plugins/customizable_commands/__init__.py +0 -0
- code_puppy/plugins/customizable_commands/register_callbacks.py +152 -0
- code_puppy/plugins/example_custom_command/README.md +280 -0
- code_puppy/plugins/example_custom_command/register_callbacks.py +48 -0
- code_puppy/plugins/file_permission_handler/__init__.py +4 -0
- code_puppy/plugins/file_permission_handler/register_callbacks.py +528 -0
- code_puppy/plugins/frontend_emitter/__init__.py +25 -0
- code_puppy/plugins/frontend_emitter/emitter.py +121 -0
- code_puppy/plugins/frontend_emitter/register_callbacks.py +261 -0
- code_puppy/plugins/hook_creator/__init__.py +1 -0
- code_puppy/plugins/hook_creator/register_callbacks.py +33 -0
- code_puppy/plugins/hook_manager/__init__.py +1 -0
- code_puppy/plugins/hook_manager/config.py +277 -0
- code_puppy/plugins/hook_manager/hooks_menu.py +551 -0
- code_puppy/plugins/hook_manager/register_callbacks.py +205 -0
- code_puppy/plugins/oauth_puppy_html.py +224 -0
- code_puppy/plugins/scheduler/__init__.py +1 -0
- code_puppy/plugins/scheduler/register_callbacks.py +88 -0
- code_puppy/plugins/scheduler/scheduler_menu.py +522 -0
- code_puppy/plugins/scheduler/scheduler_wizard.py +341 -0
- code_puppy/plugins/shell_safety/__init__.py +6 -0
- code_puppy/plugins/shell_safety/agent_shell_safety.py +69 -0
- code_puppy/plugins/shell_safety/command_cache.py +156 -0
- code_puppy/plugins/shell_safety/register_callbacks.py +202 -0
- code_puppy/plugins/synthetic_status/__init__.py +1 -0
- code_puppy/plugins/synthetic_status/register_callbacks.py +132 -0
- code_puppy/plugins/synthetic_status/status_api.py +147 -0
- code_puppy/plugins/universal_constructor/__init__.py +13 -0
- code_puppy/plugins/universal_constructor/models.py +138 -0
- code_puppy/plugins/universal_constructor/register_callbacks.py +47 -0
- code_puppy/plugins/universal_constructor/registry.py +302 -0
- code_puppy/plugins/universal_constructor/sandbox.py +584 -0
- code_puppy/prompts/antigravity_system_prompt.md +1 -0
- code_puppy/pydantic_patches.py +317 -0
- code_puppy/reopenable_async_client.py +232 -0
- code_puppy/round_robin_model.py +150 -0
- code_puppy/scheduler/__init__.py +41 -0
- code_puppy/scheduler/__main__.py +9 -0
- code_puppy/scheduler/cli.py +118 -0
- code_puppy/scheduler/config.py +126 -0
- code_puppy/scheduler/daemon.py +280 -0
- code_puppy/scheduler/executor.py +155 -0
- code_puppy/scheduler/platform.py +19 -0
- code_puppy/scheduler/platform_unix.py +22 -0
- code_puppy/scheduler/platform_win.py +32 -0
- code_puppy/session_storage.py +338 -0
- code_puppy/status_display.py +257 -0
- code_puppy/summarization_agent.py +176 -0
- code_puppy/terminal_utils.py +418 -0
- code_puppy/tools/__init__.py +470 -0
- code_puppy/tools/agent_tools.py +616 -0
- code_puppy/tools/ask_user_question/__init__.py +26 -0
- code_puppy/tools/ask_user_question/constants.py +73 -0
- code_puppy/tools/ask_user_question/demo_tui.py +55 -0
- code_puppy/tools/ask_user_question/handler.py +232 -0
- code_puppy/tools/ask_user_question/models.py +304 -0
- code_puppy/tools/ask_user_question/registration.py +36 -0
- code_puppy/tools/ask_user_question/renderers.py +309 -0
- code_puppy/tools/ask_user_question/terminal_ui.py +329 -0
- code_puppy/tools/ask_user_question/theme.py +155 -0
- code_puppy/tools/ask_user_question/tui_loop.py +423 -0
- code_puppy/tools/browser/__init__.py +37 -0
- code_puppy/tools/browser/browser_control.py +289 -0
- code_puppy/tools/browser/browser_interactions.py +545 -0
- code_puppy/tools/browser/browser_locators.py +640 -0
- code_puppy/tools/browser/browser_manager.py +378 -0
- code_puppy/tools/browser/browser_navigation.py +251 -0
- code_puppy/tools/browser/browser_screenshot.py +179 -0
- code_puppy/tools/browser/browser_scripts.py +462 -0
- code_puppy/tools/browser/browser_workflows.py +221 -0
- code_puppy/tools/browser/chromium_terminal_manager.py +259 -0
- code_puppy/tools/browser/terminal_command_tools.py +534 -0
- code_puppy/tools/browser/terminal_screenshot_tools.py +552 -0
- code_puppy/tools/browser/terminal_tools.py +525 -0
- code_puppy/tools/command_runner.py +1346 -0
- code_puppy/tools/common.py +1409 -0
- code_puppy/tools/display.py +84 -0
- code_puppy/tools/file_modifications.py +739 -0
- code_puppy/tools/file_operations.py +802 -0
- code_puppy/tools/scheduler_tools.py +412 -0
- code_puppy/tools/skills_tools.py +251 -0
- code_puppy/tools/subagent_context.py +158 -0
- code_puppy/tools/tools_content.py +51 -0
- code_puppy/tools/universal_constructor.py +889 -0
- code_puppy/uvx_detection.py +242 -0
- code_puppy/version_checker.py +82 -0
- newcode-0.1.1.data/data/code_puppy/models.json +130 -0
- newcode-0.1.1.data/data/code_puppy/models_dev_api.json +1 -0
- newcode-0.1.1.dist-info/METADATA +154 -0
- newcode-0.1.1.dist-info/RECORD +289 -0
- newcode-0.1.1.dist-info/WHEEL +4 -0
- newcode-0.1.1.dist-info/entry_points.txt +3 -0
- 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>✅ {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>❌ {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, "✨"),
|
|
132
|
+
(18, 28, 0.2, "✅"),
|
|
133
|
+
(32, 6, 1.1, "🎉"),
|
|
134
|
+
(46, 18, 0.5, "✨"),
|
|
135
|
+
(62, 9, 0.8, "💫"),
|
|
136
|
+
(76, 22, 1.3, "✅"),
|
|
137
|
+
(88, 14, 0.4, "✨"),
|
|
138
|
+
(12, 48, 0.6, "🎉"),
|
|
139
|
+
(28, 58, 1.7, "💫"),
|
|
140
|
+
(44, 42, 0.9, "✨"),
|
|
141
|
+
(58, 52, 1.5, "✅"),
|
|
142
|
+
(72, 46, 0.3, "🎉"),
|
|
143
|
+
(86, 54, 1.1, "💫"),
|
|
144
|
+
(8, 72, 0.7, "✨"),
|
|
145
|
+
(24, 80, 1.2, "🎉"),
|
|
146
|
+
(40, 74, 0.2, "✅"),
|
|
147
|
+
(56, 66, 1.6, "✨"),
|
|
148
|
+
(70, 78, 1.0, "💫"),
|
|
149
|
+
(84, 70, 1.4, "✅"),
|
|
150
|
+
(16, 90, 0.5, "✨"),
|
|
151
|
+
(32, 92, 1.9, "💫"),
|
|
152
|
+
(48, 88, 1.1, "🎉"),
|
|
153
|
+
(64, 94, 1.8, "✨"),
|
|
154
|
+
(78, 88, 0.6, "✅"),
|
|
155
|
+
(90, 82, 1.3, "💫"),
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
_FAILURE_EMOJIS = (
|
|
160
|
+
(8, 6, 0.0, "⚠"),
|
|
161
|
+
(22, 18, 0.3, "❌"),
|
|
162
|
+
(36, 10, 0.6, "⚠"),
|
|
163
|
+
(50, 20, 0.9, "❌"),
|
|
164
|
+
(64, 8, 1.2, "⚠"),
|
|
165
|
+
(78, 16, 1.5, "❌"),
|
|
166
|
+
(12, 38, 0.4, "⚠"),
|
|
167
|
+
(28, 44, 0.7, "❌"),
|
|
168
|
+
(42, 34, 1.0, "⚠"),
|
|
169
|
+
(58, 46, 1.3, "❌"),
|
|
170
|
+
(72, 36, 1.6, "⚠"),
|
|
171
|
+
(86, 40, 1.9, "❌"),
|
|
172
|
+
(16, 64, 0.5, "⚠"),
|
|
173
|
+
(32, 70, 0.8, "❌"),
|
|
174
|
+
(48, 60, 1.1, "⚠"),
|
|
175
|
+
(62, 74, 1.4, "❌"),
|
|
176
|
+
(78, 68, 1.7, "⚠"),
|
|
177
|
+
(90, 72, 2.0, "❌"),
|
|
178
|
+
(20, 88, 0.6, "⚠"),
|
|
179
|
+
(36, 92, 0.9, "❌"),
|
|
180
|
+
(52, 86, 1.2, "⚠"),
|
|
181
|
+
(68, 94, 1.5, "❌"),
|
|
182
|
+
(82, 90, 1.8, "⚠"),
|
|
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'>🚀</span><span class='cannon right'>⚡</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 "⚡", CLAUDE_LOGO_URL, "Claude logo", ""
|
|
220
|
+
if "chat" in normalized or "gpt" in normalized:
|
|
221
|
+
return "🚀", CHATGPT_LOGO_URL, "ChatGPT logo", "invert"
|
|
222
|
+
if "gemini" in normalized or "google" in normalized:
|
|
223
|
+
return "✨", GEMINI_LOGO_URL, "Gemini logo", ""
|
|
224
|
+
return "💫", 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)
|