codepp 0.0.437__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 +117 -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 +638 -0
- code_puppy/agents/agent_golang_reviewer.py +151 -0
- code_puppy/agents/agent_helios.py +124 -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 +385 -0
- code_puppy/agents/agent_planning.py +165 -0
- code_puppy/agents/agent_python_programmer.py +169 -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 +2156 -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 +304 -0
- code_puppy/agents/pack/husky.py +327 -0
- code_puppy/agents/pack/retriever.py +393 -0
- code_puppy/agents/pack/shepherd.py +348 -0
- code_puppy/agents/pack/terrier.py +287 -0
- code_puppy/agents/pack/watchdog.py +367 -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 +692 -0
- code_puppy/chatgpt_codex_client.py +338 -0
- code_puppy/claude_cache_client.py +672 -0
- code_puppy/cli_runner.py +1073 -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 +532 -0
- code_puppy/command_line/command_handler.py +293 -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 +867 -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 +96 -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/shell_passthrough.py +145 -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 +1770 -0
- code_puppy/error_logging.py +134 -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 +21 -0
- code_puppy/hook_engine/aliases.py +155 -0
- code_puppy/hook_engine/engine.py +221 -0
- code_puppy/hook_engine/executor.py +296 -0
- code_puppy/hook_engine/matcher.py +156 -0
- code_puppy/hook_engine/models.py +240 -0
- code_puppy/hook_engine/registry.py +106 -0
- code_puppy/hook_engine/validator.py +144 -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 +1158 -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 +95 -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 +174 -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 +60 -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 +329 -0
- code_puppy/plugins/chatgpt_oauth/register_callbacks.py +176 -0
- code_puppy/plugins/chatgpt_oauth/test_plugin.py +301 -0
- code_puppy/plugins/chatgpt_oauth/utils.py +523 -0
- code_puppy/plugins/claude_code_hooks/__init__.py +1 -0
- code_puppy/plugins/claude_code_hooks/config.py +137 -0
- code_puppy/plugins/claude_code_hooks/register_callbacks.py +175 -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 +640 -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 +51 -0
- code_puppy/plugins/file_permission_handler/__init__.py +4 -0
- code_puppy/plugins/file_permission_handler/register_callbacks.py +470 -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 +290 -0
- code_puppy/plugins/hook_manager/hooks_menu.py +564 -0
- code_puppy/plugins/hook_manager/register_callbacks.py +227 -0
- code_puppy/plugins/oauth_puppy_html.py +228 -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 +356 -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 +501 -0
- code_puppy/tools/agent_tools.py +603 -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 +26 -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 +886 -0
- code_puppy/tools/file_operations.py +802 -0
- code_puppy/tools/scheduler_tools.py +412 -0
- code_puppy/tools/skills_tools.py +244 -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
- codepp-0.0.437.dist-info/METADATA +766 -0
- codepp-0.0.437.dist-info/RECORD +288 -0
- codepp-0.0.437.dist-info/WHEEL +4 -0
- codepp-0.0.437.dist-info/entry_points.txt +3 -0
- codepp-0.0.437.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
"""Scheduler tools for the Scheduler Agent.
|
|
2
|
+
|
|
3
|
+
These tools allow the scheduler agent to manage scheduled tasks,
|
|
4
|
+
control the daemon, and view logs.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
|
|
9
|
+
from pydantic import Field
|
|
10
|
+
|
|
11
|
+
from code_puppy.messaging import emit_info, emit_success, emit_warning
|
|
12
|
+
from code_puppy.tools.common import generate_group_id
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def register_scheduler_list_tasks(agent):
|
|
16
|
+
"""Register the scheduler_list_tasks tool."""
|
|
17
|
+
|
|
18
|
+
def scheduler_list_tasks() -> str:
|
|
19
|
+
"""List all scheduled tasks with their status and daemon info.
|
|
20
|
+
|
|
21
|
+
Returns a formatted overview of:
|
|
22
|
+
- Daemon status (running/stopped)
|
|
23
|
+
- All configured tasks with their schedules
|
|
24
|
+
- Last run status for each task
|
|
25
|
+
"""
|
|
26
|
+
from code_puppy.scheduler import load_tasks
|
|
27
|
+
from code_puppy.scheduler.daemon import get_daemon_pid
|
|
28
|
+
|
|
29
|
+
group_id = generate_group_id("scheduler_list_tasks")
|
|
30
|
+
emit_info("📅 SCHEDULER LIST TASKS", message_group=group_id)
|
|
31
|
+
|
|
32
|
+
tasks = load_tasks()
|
|
33
|
+
pid = get_daemon_pid()
|
|
34
|
+
|
|
35
|
+
lines = []
|
|
36
|
+
lines.append("## Scheduler Status")
|
|
37
|
+
lines.append(
|
|
38
|
+
f"**Daemon:** {'🟢 Running (PID ' + str(pid) + ')' if pid else '🔴 Stopped'}"
|
|
39
|
+
)
|
|
40
|
+
lines.append(f"**Total Tasks:** {len(tasks)}")
|
|
41
|
+
lines.append("")
|
|
42
|
+
|
|
43
|
+
if not tasks:
|
|
44
|
+
lines.append("No scheduled tasks configured yet.")
|
|
45
|
+
lines.append("")
|
|
46
|
+
lines.append("Use `scheduler_create_task` to create one!")
|
|
47
|
+
return "\n".join(lines)
|
|
48
|
+
|
|
49
|
+
lines.append("## Tasks")
|
|
50
|
+
lines.append("")
|
|
51
|
+
|
|
52
|
+
for task in tasks:
|
|
53
|
+
status_icon = "🟢" if task.enabled else "🔴"
|
|
54
|
+
run_status = ""
|
|
55
|
+
if task.last_status == "success":
|
|
56
|
+
run_status = " ✅"
|
|
57
|
+
elif task.last_status == "failed":
|
|
58
|
+
run_status = " ❌"
|
|
59
|
+
elif task.last_status == "running":
|
|
60
|
+
run_status = " ⏳"
|
|
61
|
+
|
|
62
|
+
lines.append(f"### {status_icon} {task.name} (`{task.id}`){run_status}")
|
|
63
|
+
lines.append(
|
|
64
|
+
f"- **Schedule:** {task.schedule_type} ({task.schedule_value})"
|
|
65
|
+
)
|
|
66
|
+
lines.append(f"- **Agent:** {task.agent}")
|
|
67
|
+
lines.append(f"- **Model:** {task.model or '(default)'}")
|
|
68
|
+
lines.append(
|
|
69
|
+
f"- **Prompt:** {task.prompt[:100]}{'...' if len(task.prompt) > 100 else ''}"
|
|
70
|
+
)
|
|
71
|
+
lines.append(f"- **Directory:** {task.working_directory}")
|
|
72
|
+
if task.last_run:
|
|
73
|
+
lines.append(
|
|
74
|
+
f"- **Last Run:** {task.last_run[:19]} (exit code: {task.last_exit_code})"
|
|
75
|
+
)
|
|
76
|
+
lines.append("")
|
|
77
|
+
|
|
78
|
+
return "\n".join(lines)
|
|
79
|
+
|
|
80
|
+
agent.tool_plain(scheduler_list_tasks)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def register_scheduler_create_task(agent):
|
|
84
|
+
"""Register the scheduler_create_task tool."""
|
|
85
|
+
|
|
86
|
+
def scheduler_create_task(
|
|
87
|
+
name: str = Field(description="Human-readable name for the task"),
|
|
88
|
+
prompt: str = Field(description="The prompt to execute"),
|
|
89
|
+
agent: str = Field(
|
|
90
|
+
default="code-puppy",
|
|
91
|
+
description="Agent to use (e.g., code-puppy, code-reviewer, security-auditor)",
|
|
92
|
+
),
|
|
93
|
+
model: str = Field(
|
|
94
|
+
default="", description="Model to use (empty string for default)"
|
|
95
|
+
),
|
|
96
|
+
schedule_type: str = Field(
|
|
97
|
+
default="interval",
|
|
98
|
+
description="Schedule type: 'interval', 'hourly', or 'daily'",
|
|
99
|
+
),
|
|
100
|
+
schedule_value: str = Field(
|
|
101
|
+
default="1h",
|
|
102
|
+
description="Schedule value (e.g., '30m', '2h', '1d' for intervals)",
|
|
103
|
+
),
|
|
104
|
+
working_directory: str = Field(
|
|
105
|
+
default=".", description="Working directory for the task"
|
|
106
|
+
),
|
|
107
|
+
) -> str:
|
|
108
|
+
"""Create a new scheduled task.
|
|
109
|
+
|
|
110
|
+
Creates a task that will run automatically according to the specified schedule.
|
|
111
|
+
The daemon must be running for tasks to execute.
|
|
112
|
+
"""
|
|
113
|
+
from code_puppy.scheduler import ScheduledTask, add_task
|
|
114
|
+
from code_puppy.scheduler.daemon import get_daemon_pid
|
|
115
|
+
|
|
116
|
+
group_id = generate_group_id("scheduler_create_task", name)
|
|
117
|
+
emit_info(f"📅 SCHEDULER CREATE TASK → {name}", message_group=group_id)
|
|
118
|
+
|
|
119
|
+
task = ScheduledTask(
|
|
120
|
+
name=name,
|
|
121
|
+
prompt=prompt,
|
|
122
|
+
agent=agent,
|
|
123
|
+
model=model,
|
|
124
|
+
schedule_type=schedule_type,
|
|
125
|
+
schedule_value=schedule_value,
|
|
126
|
+
working_directory=working_directory,
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
add_task(task)
|
|
130
|
+
emit_success(f"Created task: {name} ({task.id})", message_group=group_id)
|
|
131
|
+
|
|
132
|
+
result = f"""✅ **Task Created Successfully!**
|
|
133
|
+
|
|
134
|
+
**ID:** `{task.id}`
|
|
135
|
+
**Name:** {task.name}
|
|
136
|
+
**Schedule:** {task.schedule_type} ({task.schedule_value})
|
|
137
|
+
**Agent:** {task.agent}
|
|
138
|
+
**Model:** {task.model or "(default)"}
|
|
139
|
+
**Directory:** {task.working_directory}
|
|
140
|
+
**Log File:** `{task.log_file}`
|
|
141
|
+
|
|
142
|
+
**Prompt:**
|
|
143
|
+
```
|
|
144
|
+
{task.prompt}
|
|
145
|
+
```
|
|
146
|
+
"""
|
|
147
|
+
|
|
148
|
+
pid = get_daemon_pid()
|
|
149
|
+
if not pid:
|
|
150
|
+
result += "\n⚠️ **Note:** The scheduler daemon is not running. Use `scheduler_start_daemon` to start it!"
|
|
151
|
+
else:
|
|
152
|
+
result += f"\n🟢 Daemon is running (PID {pid}). Task will execute according to schedule."
|
|
153
|
+
|
|
154
|
+
return result
|
|
155
|
+
|
|
156
|
+
agent.tool_plain(scheduler_create_task)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def register_scheduler_delete_task(agent):
|
|
160
|
+
"""Register the scheduler_delete_task tool."""
|
|
161
|
+
|
|
162
|
+
def scheduler_delete_task(
|
|
163
|
+
task_id: str = Field(description="The ID of the task to delete"),
|
|
164
|
+
) -> str:
|
|
165
|
+
"""Delete a scheduled task by its ID.
|
|
166
|
+
|
|
167
|
+
Permanently removes the task from the schedule.
|
|
168
|
+
The task's log file is NOT deleted.
|
|
169
|
+
"""
|
|
170
|
+
from code_puppy.scheduler import delete_task, get_task
|
|
171
|
+
|
|
172
|
+
group_id = generate_group_id("scheduler_delete_task", task_id)
|
|
173
|
+
emit_info(f"📅 SCHEDULER DELETE TASK → {task_id}", message_group=group_id)
|
|
174
|
+
|
|
175
|
+
task = get_task(task_id)
|
|
176
|
+
if not task:
|
|
177
|
+
emit_warning(f"Task not found: {task_id}", message_group=group_id)
|
|
178
|
+
return f"❌ Task not found: `{task_id}`"
|
|
179
|
+
|
|
180
|
+
task_name = task.name
|
|
181
|
+
if delete_task(task_id):
|
|
182
|
+
emit_success(f"Deleted task: {task_name}", message_group=group_id)
|
|
183
|
+
return f"✅ Deleted task: **{task_name}** (`{task_id}`)"
|
|
184
|
+
else:
|
|
185
|
+
return f"❌ Failed to delete task: `{task_id}`"
|
|
186
|
+
|
|
187
|
+
agent.tool_plain(scheduler_delete_task)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def register_scheduler_toggle_task(agent):
|
|
191
|
+
"""Register the scheduler_toggle_task tool."""
|
|
192
|
+
|
|
193
|
+
def scheduler_toggle_task(
|
|
194
|
+
task_id: str = Field(description="The ID of the task to enable/disable"),
|
|
195
|
+
) -> str:
|
|
196
|
+
"""Toggle a task's enabled/disabled state.
|
|
197
|
+
|
|
198
|
+
Disabled tasks remain in the schedule but won't run until re-enabled.
|
|
199
|
+
"""
|
|
200
|
+
from code_puppy.scheduler import get_task, toggle_task
|
|
201
|
+
|
|
202
|
+
group_id = generate_group_id("scheduler_toggle_task", task_id)
|
|
203
|
+
emit_info(f"📅 SCHEDULER TOGGLE TASK → {task_id}", message_group=group_id)
|
|
204
|
+
|
|
205
|
+
task = get_task(task_id)
|
|
206
|
+
if not task:
|
|
207
|
+
emit_warning(f"Task not found: {task_id}", message_group=group_id)
|
|
208
|
+
return f"❌ Task not found: `{task_id}`"
|
|
209
|
+
|
|
210
|
+
task_name = task.name
|
|
211
|
+
new_state = toggle_task(task_id)
|
|
212
|
+
|
|
213
|
+
if new_state is None:
|
|
214
|
+
return f"❌ Failed to toggle task: `{task_id}`"
|
|
215
|
+
|
|
216
|
+
status = "🟢 **Enabled**" if new_state else "🔴 **Disabled**"
|
|
217
|
+
status_msg = "enabled" if new_state else "disabled"
|
|
218
|
+
emit_success(f"Task {task_name} {status_msg}", message_group=group_id)
|
|
219
|
+
return f"Task **{task_name}** (`{task_id}`) is now {status}"
|
|
220
|
+
|
|
221
|
+
agent.tool_plain(scheduler_toggle_task)
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def register_scheduler_daemon_status(agent):
|
|
225
|
+
"""Register the scheduler_daemon_status tool."""
|
|
226
|
+
|
|
227
|
+
def scheduler_daemon_status() -> str:
|
|
228
|
+
"""Check if the scheduler daemon is running.
|
|
229
|
+
|
|
230
|
+
Returns detailed status including PID and task counts.
|
|
231
|
+
"""
|
|
232
|
+
from code_puppy.scheduler import load_tasks
|
|
233
|
+
from code_puppy.scheduler.daemon import get_daemon_pid
|
|
234
|
+
|
|
235
|
+
group_id = generate_group_id("scheduler_daemon_status")
|
|
236
|
+
emit_info("📅 SCHEDULER DAEMON STATUS", message_group=group_id)
|
|
237
|
+
|
|
238
|
+
pid = get_daemon_pid()
|
|
239
|
+
tasks = load_tasks()
|
|
240
|
+
enabled_count = sum(1 for t in tasks if t.enabled)
|
|
241
|
+
|
|
242
|
+
if pid:
|
|
243
|
+
return f"""🟢 **Daemon is RUNNING**
|
|
244
|
+
|
|
245
|
+
**PID:** {pid}
|
|
246
|
+
**Total Tasks:** {len(tasks)}
|
|
247
|
+
**Enabled Tasks:** {enabled_count}
|
|
248
|
+
|
|
249
|
+
The scheduler is actively monitoring and running tasks according to their schedules."""
|
|
250
|
+
else:
|
|
251
|
+
return f"""🔴 **Daemon is STOPPED**
|
|
252
|
+
|
|
253
|
+
**Total Tasks:** {len(tasks)}
|
|
254
|
+
**Enabled Tasks:** {enabled_count}
|
|
255
|
+
|
|
256
|
+
The scheduler daemon is not running. Scheduled tasks will NOT execute until you start it.
|
|
257
|
+
|
|
258
|
+
Use `scheduler_start_daemon` to start the daemon."""
|
|
259
|
+
|
|
260
|
+
agent.tool_plain(scheduler_daemon_status)
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def register_scheduler_start_daemon(agent):
|
|
264
|
+
"""Register the scheduler_start_daemon tool."""
|
|
265
|
+
|
|
266
|
+
def scheduler_start_daemon() -> str:
|
|
267
|
+
"""Start the scheduler daemon in the background.
|
|
268
|
+
|
|
269
|
+
The daemon runs independently and will continue even after
|
|
270
|
+
Code Puppy exits. It checks for and runs scheduled tasks.
|
|
271
|
+
"""
|
|
272
|
+
from code_puppy.scheduler.daemon import get_daemon_pid, start_daemon_background
|
|
273
|
+
|
|
274
|
+
group_id = generate_group_id("scheduler_start_daemon")
|
|
275
|
+
emit_info("📅 SCHEDULER START DAEMON", message_group=group_id)
|
|
276
|
+
|
|
277
|
+
pid = get_daemon_pid()
|
|
278
|
+
if pid:
|
|
279
|
+
emit_warning(f"Daemon already running (PID {pid})", message_group=group_id)
|
|
280
|
+
return f"ℹ️ Daemon is already running (PID {pid})"
|
|
281
|
+
|
|
282
|
+
if start_daemon_background():
|
|
283
|
+
new_pid = get_daemon_pid()
|
|
284
|
+
emit_success(f"Daemon started (PID {new_pid})", message_group=group_id)
|
|
285
|
+
return f"✅ **Daemon started successfully!** (PID {new_pid})\n\nScheduled tasks will now run automatically."
|
|
286
|
+
else:
|
|
287
|
+
emit_warning("Failed to start daemon", message_group=group_id)
|
|
288
|
+
return "❌ **Failed to start daemon.** Check the logs for errors."
|
|
289
|
+
|
|
290
|
+
agent.tool_plain(scheduler_start_daemon)
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def register_scheduler_stop_daemon(agent):
|
|
294
|
+
"""Register the scheduler_stop_daemon tool."""
|
|
295
|
+
|
|
296
|
+
def scheduler_stop_daemon() -> str:
|
|
297
|
+
"""Stop the scheduler daemon.
|
|
298
|
+
|
|
299
|
+
Stops the background process. No scheduled tasks will run
|
|
300
|
+
until the daemon is started again.
|
|
301
|
+
"""
|
|
302
|
+
from code_puppy.scheduler.daemon import get_daemon_pid, stop_daemon
|
|
303
|
+
|
|
304
|
+
group_id = generate_group_id("scheduler_stop_daemon")
|
|
305
|
+
emit_info("📅 SCHEDULER STOP DAEMON", message_group=group_id)
|
|
306
|
+
|
|
307
|
+
pid = get_daemon_pid()
|
|
308
|
+
if not pid:
|
|
309
|
+
emit_warning("Daemon is not running", message_group=group_id)
|
|
310
|
+
return "ℹ️ Daemon is not running."
|
|
311
|
+
|
|
312
|
+
if stop_daemon():
|
|
313
|
+
emit_success(f"Daemon stopped (was PID {pid})", message_group=group_id)
|
|
314
|
+
return f"✅ **Daemon stopped.** (was PID {pid})\n\nScheduled tasks will NOT run until you start the daemon again."
|
|
315
|
+
else:
|
|
316
|
+
emit_warning(f"Failed to stop daemon (PID {pid})", message_group=group_id)
|
|
317
|
+
return f"❌ **Failed to stop daemon** (PID {pid}). You may need to kill it manually."
|
|
318
|
+
|
|
319
|
+
agent.tool_plain(scheduler_stop_daemon)
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def register_scheduler_run_task(agent):
|
|
323
|
+
"""Register the scheduler_run_task tool."""
|
|
324
|
+
|
|
325
|
+
def scheduler_run_task(
|
|
326
|
+
task_id: str = Field(description="The ID of the task to run immediately"),
|
|
327
|
+
) -> str:
|
|
328
|
+
"""Run a scheduled task immediately.
|
|
329
|
+
|
|
330
|
+
Executes the task right now, regardless of its schedule.
|
|
331
|
+
Useful for testing or one-off runs.
|
|
332
|
+
"""
|
|
333
|
+
from code_puppy.scheduler import get_task
|
|
334
|
+
from code_puppy.scheduler.executor import run_task_by_id
|
|
335
|
+
|
|
336
|
+
group_id = generate_group_id("scheduler_run_task", task_id)
|
|
337
|
+
emit_info(f"📅 SCHEDULER RUN TASK → {task_id}", message_group=group_id)
|
|
338
|
+
|
|
339
|
+
task = get_task(task_id)
|
|
340
|
+
if not task:
|
|
341
|
+
emit_warning(f"Task not found: {task_id}", message_group=group_id)
|
|
342
|
+
return f"❌ Task not found: `{task_id}`"
|
|
343
|
+
|
|
344
|
+
emit_info(f"Running: {task.name}...", message_group=group_id)
|
|
345
|
+
result = f"⏳ Running task **{task.name}** (`{task_id}`)...\n\n"
|
|
346
|
+
|
|
347
|
+
success, message = run_task_by_id(task_id)
|
|
348
|
+
|
|
349
|
+
if success:
|
|
350
|
+
emit_success(f"Task completed: {task.name}", message_group=group_id)
|
|
351
|
+
result += f"✅ **Task completed successfully!**\n\n{message}\n\nView the log with `scheduler_view_log`."
|
|
352
|
+
else:
|
|
353
|
+
emit_warning(f"Task failed: {task.name}", message_group=group_id)
|
|
354
|
+
result += f"❌ **Task failed.**\n\n{message}\n\nCheck the log with `scheduler_view_log` for details."
|
|
355
|
+
|
|
356
|
+
return result
|
|
357
|
+
|
|
358
|
+
agent.tool_plain(scheduler_run_task)
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
def register_scheduler_view_log(agent):
|
|
362
|
+
"""Register the scheduler_view_log tool."""
|
|
363
|
+
|
|
364
|
+
def scheduler_view_log(
|
|
365
|
+
task_id: str = Field(description="The ID of the task whose log to view"),
|
|
366
|
+
lines: int = Field(
|
|
367
|
+
default=50, description="Number of lines to show from the end of the log"
|
|
368
|
+
),
|
|
369
|
+
) -> str:
|
|
370
|
+
"""View the log file for a scheduled task.
|
|
371
|
+
|
|
372
|
+
Shows the most recent output from task executions.
|
|
373
|
+
Each run appends to the log file.
|
|
374
|
+
"""
|
|
375
|
+
from code_puppy.scheduler import get_task
|
|
376
|
+
|
|
377
|
+
group_id = generate_group_id("scheduler_view_log", task_id)
|
|
378
|
+
emit_info(
|
|
379
|
+
f"📅 SCHEDULER VIEW LOG → {task_id} (last {lines} lines)",
|
|
380
|
+
message_group=group_id,
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
task = get_task(task_id)
|
|
384
|
+
if not task:
|
|
385
|
+
emit_warning(f"Task not found: {task_id}", message_group=group_id)
|
|
386
|
+
return f"❌ Task not found: `{task_id}`"
|
|
387
|
+
|
|
388
|
+
log_file = task.log_file
|
|
389
|
+
if not os.path.exists(log_file):
|
|
390
|
+
return f"📄 **No log file yet for task:** {task.name} (`{task_id}`)\n\nThe log will be created when the task runs for the first time."
|
|
391
|
+
|
|
392
|
+
try:
|
|
393
|
+
with open(log_file, "r") as f:
|
|
394
|
+
content = f.read()
|
|
395
|
+
|
|
396
|
+
log_lines = content.split("\n")
|
|
397
|
+
if len(log_lines) > lines:
|
|
398
|
+
log_lines = log_lines[-lines:]
|
|
399
|
+
|
|
400
|
+
truncated_content = "\n".join(log_lines)
|
|
401
|
+
|
|
402
|
+
return f"""📄 **Log for task:** {task.name} (`{task_id}`)
|
|
403
|
+
**File:** `{log_file}`
|
|
404
|
+
**Showing:** last {lines} lines
|
|
405
|
+
|
|
406
|
+
```
|
|
407
|
+
{truncated_content}
|
|
408
|
+
```"""
|
|
409
|
+
except Exception as e:
|
|
410
|
+
return f"❌ Error reading log file: {e}"
|
|
411
|
+
|
|
412
|
+
agent.tool_plain(scheduler_view_log)
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
"""Skills tools - dedicated tools for Agent Skills integration."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from typing import List, Optional
|
|
5
|
+
|
|
6
|
+
from pydantic import BaseModel
|
|
7
|
+
from pydantic_ai import RunContext
|
|
8
|
+
|
|
9
|
+
from code_puppy.messaging import (
|
|
10
|
+
SkillActivateMessage,
|
|
11
|
+
SkillEntry,
|
|
12
|
+
SkillListMessage,
|
|
13
|
+
get_message_bus,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# Output models
|
|
20
|
+
class SkillListOutput(BaseModel):
|
|
21
|
+
"""Output for list_or_search_skills tool."""
|
|
22
|
+
|
|
23
|
+
skills: List[dict] # Each has: name, description, path, tags
|
|
24
|
+
total_count: int
|
|
25
|
+
query: Optional[str] = None # The search query if provided
|
|
26
|
+
error: Optional[str] = None
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class SkillActivateOutput(BaseModel):
|
|
30
|
+
"""Output for activate_skill tool."""
|
|
31
|
+
|
|
32
|
+
skill_name: str
|
|
33
|
+
content: str # Full SKILL.md content
|
|
34
|
+
resources: List[str] # Available resource files
|
|
35
|
+
error: Optional[str] = None
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def register_activate_skill(agent):
|
|
39
|
+
"""Register the activate_skill tool."""
|
|
40
|
+
|
|
41
|
+
@agent.tool
|
|
42
|
+
async def activate_skill(
|
|
43
|
+
context: RunContext, skill_name: str = ""
|
|
44
|
+
) -> SkillActivateOutput:
|
|
45
|
+
"""Activate a skill by loading its full SKILL.md instructions."""
|
|
46
|
+
# Import from plugin
|
|
47
|
+
from pathlib import Path
|
|
48
|
+
|
|
49
|
+
from code_puppy.plugins.agent_skills.config import (
|
|
50
|
+
get_skill_directories,
|
|
51
|
+
get_skills_enabled,
|
|
52
|
+
)
|
|
53
|
+
from code_puppy.plugins.agent_skills.discovery import discover_skills
|
|
54
|
+
from code_puppy.plugins.agent_skills.metadata import (
|
|
55
|
+
get_skill_resources,
|
|
56
|
+
load_full_skill_content,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
# Check if skills enabled
|
|
60
|
+
if not get_skills_enabled():
|
|
61
|
+
return SkillActivateOutput(
|
|
62
|
+
skill_name=skill_name,
|
|
63
|
+
content="",
|
|
64
|
+
resources=[],
|
|
65
|
+
error="Skills integration is disabled. Enable it with /set skills_enabled=true",
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
# Discover skills
|
|
69
|
+
try:
|
|
70
|
+
skill_dirs = [Path(d) for d in get_skill_directories()]
|
|
71
|
+
discovered = discover_skills(skill_dirs)
|
|
72
|
+
except Exception as e:
|
|
73
|
+
logger.error(f"Failed to discover skills: {e}")
|
|
74
|
+
return SkillActivateOutput(
|
|
75
|
+
skill_name=skill_name,
|
|
76
|
+
content="",
|
|
77
|
+
resources=[],
|
|
78
|
+
error=f"Failed to discover skills: {e}",
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
# Find skill by name
|
|
82
|
+
skill_path = None
|
|
83
|
+
for skill_info in discovered:
|
|
84
|
+
if skill_info.name == skill_name and skill_info.has_skill_md:
|
|
85
|
+
skill_path = skill_info.path
|
|
86
|
+
break
|
|
87
|
+
|
|
88
|
+
if not skill_path:
|
|
89
|
+
return SkillActivateOutput(
|
|
90
|
+
skill_name=skill_name,
|
|
91
|
+
content="",
|
|
92
|
+
resources=[],
|
|
93
|
+
error=f"Skill '{skill_name}' not found. Use list_or_search_skills to see available skills.",
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
# Load full content
|
|
97
|
+
content = load_full_skill_content(skill_path)
|
|
98
|
+
if content is None:
|
|
99
|
+
return SkillActivateOutput(
|
|
100
|
+
skill_name=skill_name,
|
|
101
|
+
content="",
|
|
102
|
+
resources=[],
|
|
103
|
+
error=f"Failed to load content for skill '{skill_name}'",
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
# Get resource list
|
|
107
|
+
resource_paths = get_skill_resources(skill_path)
|
|
108
|
+
resources = [str(p) for p in resource_paths]
|
|
109
|
+
|
|
110
|
+
# Emit message for UI
|
|
111
|
+
content_preview = content[:200] if content else ""
|
|
112
|
+
skill_msg = SkillActivateMessage(
|
|
113
|
+
skill_name=skill_name,
|
|
114
|
+
skill_path=str(skill_path),
|
|
115
|
+
content_preview=content_preview,
|
|
116
|
+
resource_count=len(resources),
|
|
117
|
+
success=True,
|
|
118
|
+
)
|
|
119
|
+
get_message_bus().emit(skill_msg)
|
|
120
|
+
|
|
121
|
+
return SkillActivateOutput(
|
|
122
|
+
skill_name=skill_name, content=content, resources=resources, error=None
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
return activate_skill
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def register_list_or_search_skills(agent):
|
|
129
|
+
"""Register the list_or_search_skills tool."""
|
|
130
|
+
|
|
131
|
+
@agent.tool
|
|
132
|
+
async def list_or_search_skills(
|
|
133
|
+
context: RunContext, query: Optional[str] = None
|
|
134
|
+
) -> SkillListOutput:
|
|
135
|
+
"""List available skills, optionally filtered by search query.
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
query: Optional search term to filter skills by name/description/tags.
|
|
139
|
+
If None, returns all available skills.
|
|
140
|
+
"""
|
|
141
|
+
# Import from plugin
|
|
142
|
+
from pathlib import Path
|
|
143
|
+
|
|
144
|
+
from code_puppy.plugins.agent_skills.config import (
|
|
145
|
+
get_disabled_skills,
|
|
146
|
+
get_skill_directories,
|
|
147
|
+
get_skills_enabled,
|
|
148
|
+
)
|
|
149
|
+
from code_puppy.plugins.agent_skills.discovery import discover_skills
|
|
150
|
+
from code_puppy.plugins.agent_skills.metadata import parse_skill_metadata
|
|
151
|
+
|
|
152
|
+
# Check if skills enabled
|
|
153
|
+
if not get_skills_enabled():
|
|
154
|
+
return SkillListOutput(
|
|
155
|
+
skills=[],
|
|
156
|
+
total_count=0,
|
|
157
|
+
query=query,
|
|
158
|
+
error="Skills integration is disabled. Enable it with /set skills_enabled=true",
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
# Get disabled skills
|
|
162
|
+
disabled_skills = get_disabled_skills()
|
|
163
|
+
|
|
164
|
+
# Discover all skills
|
|
165
|
+
try:
|
|
166
|
+
skill_dirs = [Path(d) for d in get_skill_directories()]
|
|
167
|
+
discovered = discover_skills(skill_dirs)
|
|
168
|
+
except Exception as e:
|
|
169
|
+
logger.error(f"Failed to discover skills: {e}")
|
|
170
|
+
return SkillListOutput(
|
|
171
|
+
skills=[],
|
|
172
|
+
total_count=0,
|
|
173
|
+
query=query,
|
|
174
|
+
error=f"Failed to discover skills: {e}",
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
# Parse metadata for each skill
|
|
178
|
+
skills_list = []
|
|
179
|
+
for skill_info in discovered:
|
|
180
|
+
# Skip disabled skills
|
|
181
|
+
if skill_info.name in disabled_skills:
|
|
182
|
+
continue
|
|
183
|
+
|
|
184
|
+
# Only include skills with valid SKILL.md
|
|
185
|
+
if not skill_info.has_skill_md:
|
|
186
|
+
continue
|
|
187
|
+
|
|
188
|
+
metadata = parse_skill_metadata(skill_info.path)
|
|
189
|
+
if metadata:
|
|
190
|
+
skill_dict = {
|
|
191
|
+
"name": metadata.name,
|
|
192
|
+
"description": metadata.description,
|
|
193
|
+
"path": str(metadata.path),
|
|
194
|
+
"tags": metadata.tags,
|
|
195
|
+
"version": metadata.version,
|
|
196
|
+
"author": metadata.author,
|
|
197
|
+
}
|
|
198
|
+
skills_list.append(skill_dict)
|
|
199
|
+
|
|
200
|
+
# Filter by query if provided
|
|
201
|
+
if query:
|
|
202
|
+
query_lower = query.lower()
|
|
203
|
+
filtered = []
|
|
204
|
+
for skill in skills_list:
|
|
205
|
+
# Check name (case-insensitive)
|
|
206
|
+
if query_lower in skill["name"].lower():
|
|
207
|
+
filtered.append(skill)
|
|
208
|
+
continue
|
|
209
|
+
|
|
210
|
+
# Check description (case-insensitive)
|
|
211
|
+
if query_lower in skill["description"].lower():
|
|
212
|
+
filtered.append(skill)
|
|
213
|
+
continue
|
|
214
|
+
|
|
215
|
+
# Check tags (case-insensitive)
|
|
216
|
+
for tag in skill["tags"]:
|
|
217
|
+
if query_lower in tag.lower():
|
|
218
|
+
filtered.append(skill)
|
|
219
|
+
break
|
|
220
|
+
skills_list = filtered
|
|
221
|
+
|
|
222
|
+
# Emit message for UI
|
|
223
|
+
skill_entries = [
|
|
224
|
+
SkillEntry(
|
|
225
|
+
name=s["name"],
|
|
226
|
+
description=s["description"],
|
|
227
|
+
path=s["path"],
|
|
228
|
+
tags=s["tags"],
|
|
229
|
+
enabled=s["name"] not in disabled_skills,
|
|
230
|
+
)
|
|
231
|
+
for s in skills_list
|
|
232
|
+
]
|
|
233
|
+
skill_msg = SkillListMessage(
|
|
234
|
+
skills=skill_entries,
|
|
235
|
+
query=query,
|
|
236
|
+
total_count=len(skills_list),
|
|
237
|
+
)
|
|
238
|
+
get_message_bus().emit(skill_msg)
|
|
239
|
+
|
|
240
|
+
return SkillListOutput(
|
|
241
|
+
skills=skills_list, total_count=len(skills_list), query=query, error=None
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
return list_or_search_skills
|