code-muse 0.0.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_muse/__init__.py +26 -0
- code_muse/__main__.py +10 -0
- code_muse/agents/__init__.py +31 -0
- code_muse/agents/_builder.py +214 -0
- code_muse/agents/_compaction.py +506 -0
- code_muse/agents/_diagnostics.py +171 -0
- code_muse/agents/_history.py +382 -0
- code_muse/agents/_key_listeners.py +148 -0
- code_muse/agents/_non_streaming_render.py +148 -0
- code_muse/agents/_runtime.py +596 -0
- code_muse/agents/agent_creator_agent.py +603 -0
- code_muse/agents/agent_helios.py +47 -0
- code_muse/agents/agent_manager.py +740 -0
- code_muse/agents/agent_muse.py +78 -0
- code_muse/agents/agent_planning.py +44 -0
- code_muse/agents/agent_qa_melpomene.py +207 -0
- code_muse/agents/base_agent.py +194 -0
- code_muse/agents/event_stream_handler.py +361 -0
- code_muse/agents/json_agent.py +201 -0
- code_muse/agents/prompt_v3.py +521 -0
- code_muse/agents/subagent_stream_handler.py +273 -0
- code_muse/callbacks.py +941 -0
- code_muse/chatgpt_codex_client.py +333 -0
- code_muse/claude_cache_client.py +853 -0
- code_muse/cli_runner/__init__.py +319 -0
- code_muse/cli_runner/args.py +63 -0
- code_muse/cli_runner/loop.py +510 -0
- code_muse/cli_runner/resume.py +72 -0
- code_muse/cli_runner/runner.py +161 -0
- code_muse/command_line/__init__.py +1 -0
- code_muse/command_line/add_model_menu.py +1331 -0
- code_muse/command_line/agent_menu.py +674 -0
- code_muse/command_line/attachments.py +397 -0
- code_muse/command_line/autosave_menu.py +709 -0
- code_muse/command_line/clipboard.py +528 -0
- code_muse/command_line/colors_menu.py +530 -0
- code_muse/command_line/command_handler.py +262 -0
- code_muse/command_line/command_registry.py +150 -0
- code_muse/command_line/config_commands.py +711 -0
- code_muse/command_line/core_commands.py +740 -0
- code_muse/command_line/diff_menu.py +865 -0
- code_muse/command_line/file_path_completion.py +73 -0
- code_muse/command_line/load_context_completion.py +57 -0
- code_muse/command_line/model_picker_completion.py +512 -0
- code_muse/command_line/model_settings_menu.py +983 -0
- code_muse/command_line/onboarding_slides.py +162 -0
- code_muse/command_line/onboarding_wizard.py +337 -0
- code_muse/command_line/pagination.py +41 -0
- code_muse/command_line/pin_command_completion.py +329 -0
- code_muse/command_line/prompt_toolkit_completion.py +886 -0
- code_muse/command_line/session_commands.py +304 -0
- code_muse/command_line/shell_passthrough.py +145 -0
- code_muse/command_line/skills_completion.py +158 -0
- code_muse/command_line/types.py +18 -0
- code_muse/command_line/uc_menu.py +908 -0
- code_muse/command_line/utils.py +105 -0
- code_muse/command_line/wiggum_state.py +77 -0
- code_muse/config.py +1138 -0
- code_muse/config_agent.py +168 -0
- code_muse/config_appearance.py +241 -0
- code_muse/config_model.py +357 -0
- code_muse/config_security.py +73 -0
- code_muse/error_logging.py +132 -0
- code_muse/evals/__init__.py +35 -0
- code_muse/evals/eval_helpers.py +81 -0
- code_muse/evals/eval_runner.py +299 -0
- code_muse/evals/sample_evals/__init__.py +1 -0
- code_muse/evals/sample_evals/eval_frugal_reads.py +59 -0
- code_muse/evals/sample_evals/eval_memory_planning.py +31 -0
- code_muse/evals/sample_evals/eval_shell_efficiency.py +39 -0
- code_muse/evals/sample_evals/eval_tool_masking.py +33 -0
- code_muse/fs_scan_cache/__init__.py +31 -0
- code_muse/fs_scan_cache/invalidation_hooks.py +89 -0
- code_muse/fs_scan_cache/scan_cache_core.cpython-314-darwin.so +0 -0
- code_muse/fs_scan_cache/scan_cache_core.pyx +203 -0
- code_muse/fs_scan_cache/tool_integration.py +309 -0
- code_muse/fs_scan_cache/ttl_policy.py +44 -0
- code_muse/gemini_code_assist.py +383 -0
- code_muse/gemini_model.py +838 -0
- code_muse/hook_engine/README.md +105 -0
- code_muse/hook_engine/__init__.py +21 -0
- code_muse/hook_engine/aliases.py +153 -0
- code_muse/hook_engine/engine.py +221 -0
- code_muse/hook_engine/executor.py +347 -0
- code_muse/hook_engine/matcher.py +154 -0
- code_muse/hook_engine/models.py +245 -0
- code_muse/hook_engine/registry.py +114 -0
- code_muse/hook_engine/trust.py +268 -0
- code_muse/hook_engine/validator.py +144 -0
- code_muse/http_utils.py +360 -0
- code_muse/keymap.py +128 -0
- code_muse/list_filtering.py +26 -0
- code_muse/main.py +10 -0
- code_muse/messaging/__init__.py +259 -0
- code_muse/messaging/bus.py +621 -0
- code_muse/messaging/commands.py +166 -0
- code_muse/messaging/markdown_patches.py +57 -0
- code_muse/messaging/message_queue.py +397 -0
- code_muse/messaging/messages.py +591 -0
- code_muse/messaging/queue_console.py +269 -0
- code_muse/messaging/renderers.py +308 -0
- code_muse/messaging/rich_renderer.py +1158 -0
- code_muse/messaging/shimmer.py +154 -0
- code_muse/messaging/spinner/__init__.py +87 -0
- code_muse/messaging/spinner/console_spinner.py +250 -0
- code_muse/messaging/spinner/spinner_base.py +82 -0
- code_muse/messaging/subagent_console.py +458 -0
- code_muse/model_factory.py +1203 -0
- code_muse/model_switching.py +59 -0
- code_muse/model_utils.py +156 -0
- code_muse/models.json +66 -0
- code_muse/models_cache/__init__.py +26 -0
- code_muse/models_cache/blocking_lru_cache.py +98 -0
- code_muse/models_cache/cache_writer.py +86 -0
- code_muse/models_cache/sha256_hash.cpython-314-darwin.so +0 -0
- code_muse/models_cache/sha256_hash.pyx +34 -0
- code_muse/models_cache/startup_integration.py +75 -0
- code_muse/models_dev_api.json +1 -0
- code_muse/models_dev_parser.py +590 -0
- code_muse/motion.py +126 -0
- code_muse/plugins/__init__.py +471 -0
- code_muse/plugins/agent_skills/__init__.py +32 -0
- code_muse/plugins/agent_skills/config.py +176 -0
- code_muse/plugins/agent_skills/discovery.py +309 -0
- code_muse/plugins/agent_skills/downloader.py +389 -0
- code_muse/plugins/agent_skills/installer.py +19 -0
- code_muse/plugins/agent_skills/metadata.py +293 -0
- code_muse/plugins/agent_skills/prompt_builder.py +66 -0
- code_muse/plugins/agent_skills/register_callbacks.py +298 -0
- code_muse/plugins/agent_skills/remote_catalog.py +320 -0
- code_muse/plugins/agent_skills/skill_catalog.py +254 -0
- code_muse/plugins/agent_skills/skills_install_menu.py +690 -0
- code_muse/plugins/agent_skills/skills_menu.py +791 -0
- code_muse/plugins/autonomous_memory/__init__.py +39 -0
- code_muse/plugins/autonomous_memory/bm25_scorer.cpython-314-darwin.so +0 -0
- code_muse/plugins/autonomous_memory/bm25_scorer.cpython-314-x86_64-linux-gnu.so +0 -0
- code_muse/plugins/autonomous_memory/bm25_scorer.pyx +291 -0
- code_muse/plugins/autonomous_memory/consolidation.py +82 -0
- code_muse/plugins/autonomous_memory/extraction.py +382 -0
- code_muse/plugins/autonomous_memory/lease_lock.py +105 -0
- code_muse/plugins/autonomous_memory/memory_injection.py +59 -0
- code_muse/plugins/autonomous_memory/register_callbacks.py +268 -0
- code_muse/plugins/autonomous_memory/secret_scanner.py +62 -0
- code_muse/plugins/autonomous_memory/session_scanner.py +163 -0
- code_muse/plugins/aws_bedrock/__init__.py +14 -0
- code_muse/plugins/aws_bedrock/config.py +99 -0
- code_muse/plugins/aws_bedrock/register_callbacks.py +241 -0
- code_muse/plugins/aws_bedrock/utils.py +153 -0
- code_muse/plugins/azure_foundry/README.md +238 -0
- code_muse/plugins/azure_foundry/__init__.py +15 -0
- code_muse/plugins/azure_foundry/config.py +125 -0
- code_muse/plugins/azure_foundry/discovery.py +187 -0
- code_muse/plugins/azure_foundry/register_callbacks.py +495 -0
- code_muse/plugins/azure_foundry/token.py +180 -0
- code_muse/plugins/azure_foundry/utils.py +345 -0
- code_muse/plugins/build_filter/__init__.py +1 -0
- code_muse/plugins/build_filter/register_callbacks.py +201 -0
- code_muse/plugins/build_filter/strategies/__init__.py +1 -0
- code_muse/plugins/build_filter/strategies/build.py +397 -0
- code_muse/plugins/chatgpt_oauth/__init__.py +6 -0
- code_muse/plugins/chatgpt_oauth/config.py +52 -0
- code_muse/plugins/chatgpt_oauth/oauth_flow.py +338 -0
- code_muse/plugins/chatgpt_oauth/register_callbacks.py +172 -0
- code_muse/plugins/chatgpt_oauth/test_plugin.py +301 -0
- code_muse/plugins/chatgpt_oauth/utils.py +538 -0
- code_muse/plugins/checkpointing/__init__.py +29 -0
- code_muse/plugins/checkpointing/checkpoint_hook.py +51 -0
- code_muse/plugins/checkpointing/conversation_snapshots.py +117 -0
- code_muse/plugins/checkpointing/register_callbacks.py +51 -0
- code_muse/plugins/checkpointing/restore_command.py +263 -0
- code_muse/plugins/checkpointing/rewind_shortcut.py +88 -0
- code_muse/plugins/checkpointing/shadow_git.py +90 -0
- code_muse/plugins/claude_code_hooks/__init__.py +1 -0
- code_muse/plugins/claude_code_hooks/config.py +188 -0
- code_muse/plugins/claude_code_hooks/register_callbacks.py +208 -0
- code_muse/plugins/claude_code_oauth/README.md +167 -0
- code_muse/plugins/claude_code_oauth/SETUP.md +93 -0
- code_muse/plugins/claude_code_oauth/__init__.py +25 -0
- code_muse/plugins/claude_code_oauth/config.py +52 -0
- code_muse/plugins/claude_code_oauth/fast_mode.py +124 -0
- code_muse/plugins/claude_code_oauth/prompt_handler.py +63 -0
- code_muse/plugins/claude_code_oauth/register_callbacks.py +547 -0
- code_muse/plugins/claude_code_oauth/test_fast_mode.py +165 -0
- code_muse/plugins/claude_code_oauth/test_plugin.py +283 -0
- code_muse/plugins/claude_code_oauth/token_refresh_heartbeat.py +237 -0
- code_muse/plugins/claude_code_oauth/utils.py +664 -0
- code_muse/plugins/copilot_auth/__init__.py +11 -0
- code_muse/plugins/copilot_auth/config.py +91 -0
- code_muse/plugins/copilot_auth/reasoning_client.py +409 -0
- code_muse/plugins/copilot_auth/register_callbacks.py +461 -0
- code_muse/plugins/copilot_auth/utils.py +584 -0
- code_muse/plugins/custom_commands/__init__.py +14 -0
- code_muse/plugins/custom_commands/args_injection.py +82 -0
- code_muse/plugins/custom_commands/command_discovery.py +89 -0
- code_muse/plugins/custom_commands/command_toml_schema.py +71 -0
- code_muse/plugins/custom_commands/register_callbacks.py +176 -0
- code_muse/plugins/customizable_commands/__init__.py +0 -0
- code_muse/plugins/customizable_commands/register_callbacks.py +136 -0
- code_muse/plugins/destructive_command_guard/__init__.py +14 -0
- code_muse/plugins/destructive_command_guard/detector.py +375 -0
- code_muse/plugins/destructive_command_guard/register_callbacks.py +148 -0
- code_muse/plugins/example_custom_command/README.md +280 -0
- code_muse/plugins/example_custom_command/register_callbacks.py +51 -0
- code_muse/plugins/file_permission_handler/__init__.py +4 -0
- code_muse/plugins/file_permission_handler/register_callbacks.py +441 -0
- code_muse/plugins/filter_engine/__init__.py +30 -0
- code_muse/plugins/filter_engine/classifier.py +153 -0
- code_muse/plugins/filter_engine/content_detector.py +184 -0
- code_muse/plugins/filter_engine/dispatcher.py +244 -0
- code_muse/plugins/filter_engine/register_callbacks.py +188 -0
- code_muse/plugins/filter_engine/registry.py +279 -0
- code_muse/plugins/filter_engine/strategies/__init__.py +8 -0
- code_muse/plugins/filter_engine/strategies/ast_compressor.cpython-314-darwin.so +0 -0
- code_muse/plugins/filter_engine/strategies/ast_compressor.cpython-314-x86_64-linux-gnu.so +0 -0
- code_muse/plugins/filter_engine/strategies/ast_compressor.pyx +348 -0
- code_muse/plugins/filter_engine/strategies/ast_parser.py +167 -0
- code_muse/plugins/filter_engine/strategies/code.cpython-314-darwin.so +0 -0
- code_muse/plugins/filter_engine/strategies/code.cpython-314-x86_64-linux-gnu.so +0 -0
- code_muse/plugins/filter_engine/strategies/code.pyx +584 -0
- code_muse/plugins/filter_engine/strategies/git.cpython-314-darwin.so +0 -0
- code_muse/plugins/filter_engine/strategies/git.cpython-314-x86_64-linux-gnu.so +0 -0
- code_muse/plugins/filter_engine/strategies/git.pyx +438 -0
- code_muse/plugins/filter_engine/strategies/json_compressor.cpython-314-darwin.so +0 -0
- code_muse/plugins/filter_engine/strategies/json_compressor.pyx +253 -0
- code_muse/plugins/filter_engine/strategies/json_patterns.cpython-314-darwin.so +0 -0
- code_muse/plugins/filter_engine/strategies/json_patterns.pyx +178 -0
- code_muse/plugins/filter_engine/strategies/lint.cpython-314-darwin.so +0 -0
- code_muse/plugins/filter_engine/strategies/lint.cpython-314-x86_64-linux-gnu.so +0 -0
- code_muse/plugins/filter_engine/strategies/lint.pyx +626 -0
- code_muse/plugins/filter_engine/strategies/test.cpython-314-darwin.so +0 -0
- code_muse/plugins/filter_engine/strategies/test.cpython-314-x86_64-linux-gnu.so +0 -0
- code_muse/plugins/filter_engine/strategies/test.pyx +431 -0
- code_muse/plugins/filter_engine/verbosity.py +63 -0
- code_muse/plugins/force_push_guard/__init__.py +5 -0
- code_muse/plugins/force_push_guard/detector.py +96 -0
- code_muse/plugins/force_push_guard/register_callbacks.py +144 -0
- code_muse/plugins/force_push_guard/test_detector.py +143 -0
- code_muse/plugins/frontend_emitter/__init__.py +25 -0
- code_muse/plugins/frontend_emitter/emitter.py +121 -0
- code_muse/plugins/frontend_emitter/register_callbacks.py +259 -0
- code_muse/plugins/gac/__init__.py +4 -0
- code_muse/plugins/gac/git_ops.py +136 -0
- code_muse/plugins/gac/prompt.py +191 -0
- code_muse/plugins/gac/register_callbacks.py +82 -0
- code_muse/plugins/hook_creator/__init__.py +1 -0
- code_muse/plugins/hook_creator/register_callbacks.py +34 -0
- code_muse/plugins/hook_manager/__init__.py +1 -0
- code_muse/plugins/hook_manager/config.py +289 -0
- code_muse/plugins/hook_manager/hooks_menu.py +563 -0
- code_muse/plugins/hook_manager/register_callbacks.py +227 -0
- code_muse/plugins/hook_monitor/register_callbacks.py +36 -0
- code_muse/plugins/mindpack/__init__.py +0 -0
- code_muse/plugins/mindpack/factory.py +930 -0
- code_muse/plugins/mindpack/judge.py +573 -0
- code_muse/plugins/mindpack/memory.py +100 -0
- code_muse/plugins/mindpack/mindpack_menu.py +1552 -0
- code_muse/plugins/mindpack/orchestration.py +605 -0
- code_muse/plugins/mindpack/register_callbacks.py +175 -0
- code_muse/plugins/mindpack/schemas.py +358 -0
- code_muse/plugins/mindpack/tools.py +387 -0
- code_muse/plugins/oauth_muse_html.py +226 -0
- code_muse/plugins/ollama_setup/__init__.py +5 -0
- code_muse/plugins/ollama_setup/completer.py +36 -0
- code_muse/plugins/ollama_setup/register_callbacks.py +410 -0
- code_muse/plugins/plan_command/__init__.py +0 -0
- code_muse/plugins/plan_command/register_callbacks.py +206 -0
- code_muse/plugins/plan_mode/__init__.py +37 -0
- code_muse/plugins/plan_mode/mode_cycling.py +40 -0
- code_muse/plugins/plan_mode/plan_generation.py +68 -0
- code_muse/plugins/plan_mode/plan_hooks.py +74 -0
- code_muse/plugins/plan_mode/plan_mode_tools.py +138 -0
- code_muse/plugins/plan_mode/register_callbacks.py +121 -0
- code_muse/plugins/plugin_trust/register_callbacks.py +140 -0
- code_muse/plugins/policy_engine/__init__.py +46 -0
- code_muse/plugins/policy_engine/approval_flow_integration.py +59 -0
- code_muse/plugins/policy_engine/policy_evaluator.py +75 -0
- code_muse/plugins/policy_engine/policy_file_discovery.py +90 -0
- code_muse/plugins/policy_engine/policy_toml_schema.py +115 -0
- code_muse/plugins/policy_engine/register_callbacks.py +112 -0
- code_muse/plugins/pop_command/__init__.py +1 -0
- code_muse/plugins/pop_command/register_callbacks.py +189 -0
- code_muse/plugins/prompt_newline/__init__.py +13 -0
- code_muse/plugins/prompt_newline/config.py +19 -0
- code_muse/plugins/prompt_newline/register_callbacks.py +159 -0
- code_muse/plugins/safety_status/__init__.py +0 -0
- code_muse/plugins/safety_status/register_callbacks.py +113 -0
- code_muse/plugins/semantic_compression/__init__.py +6 -0
- code_muse/plugins/semantic_compression/compressor.py +295 -0
- code_muse/plugins/semantic_compression/config.py +123 -0
- code_muse/plugins/semantic_compression/register_callbacks.py +320 -0
- code_muse/plugins/shell_minimizer/__init__.py +50 -0
- code_muse/plugins/shell_minimizer/builtin_filters.toml +393 -0
- code_muse/plugins/shell_minimizer/pipeline.py +556 -0
- code_muse/plugins/shell_minimizer/primitives.py +482 -0
- code_muse/plugins/shell_minimizer/register_callbacks.py +276 -0
- code_muse/plugins/shell_safety/__init__.py +6 -0
- code_muse/plugins/shell_safety/agent_shell_safety.py +69 -0
- code_muse/plugins/shell_safety/command_cache.py +149 -0
- code_muse/plugins/shell_safety/register_callbacks.py +202 -0
- code_muse/plugins/synthetic_status/__init__.py +1 -0
- code_muse/plugins/synthetic_status/register_callbacks.py +128 -0
- code_muse/plugins/synthetic_status/status_api.py +145 -0
- code_muse/plugins/token_caching/__init__.py +21 -0
- code_muse/plugins/token_caching/cache_hit_tracking.py +128 -0
- code_muse/plugins/token_caching/cacheable_prefix_detection.py +28 -0
- code_muse/plugins/token_caching/register_callbacks.py +54 -0
- code_muse/plugins/token_caching/stats_display.py +35 -0
- code_muse/plugins/token_tracking/__init__.py +26 -0
- code_muse/plugins/token_tracking/database.py +381 -0
- code_muse/plugins/token_tracking/edit_analyzer.py +97 -0
- code_muse/plugins/token_tracking/record.py +55 -0
- code_muse/plugins/token_tracking/register_callbacks.py +277 -0
- code_muse/plugins/token_tracking/reports.py +329 -0
- code_muse/plugins/universal_constructor/__init__.py +13 -0
- code_muse/plugins/universal_constructor/models.py +136 -0
- code_muse/plugins/universal_constructor/register_callbacks.py +47 -0
- code_muse/plugins/universal_constructor/registry.py +390 -0
- code_muse/plugins/universal_constructor/runner.py +474 -0
- code_muse/plugins/universal_constructor/safety.py +440 -0
- code_muse/plugins/universal_constructor/sandbox.py +584 -0
- code_muse/provider_identity.py +105 -0
- code_muse/pydantic_patches.py +410 -0
- code_muse/reopenable_async_client.py +233 -0
- code_muse/round_robin_model.py +151 -0
- code_muse/secret_storage.py +74 -0
- code_muse/security/__init__.py +1 -0
- code_muse/security/redaction.cpython-314-darwin.so +0 -0
- code_muse/security/redaction.cpython-314-x86_64-linux-gnu.so +0 -0
- code_muse/security/redaction.pyx +135 -0
- code_muse/session_storage.py +565 -0
- code_muse/status_display.py +261 -0
- code_muse/stream_parser/__init__.py +76 -0
- code_muse/stream_parser/assistant_text_parser.py +90 -0
- code_muse/stream_parser/citation_parser.py +76 -0
- code_muse/stream_parser/inline_hidden_tag_parser.py +236 -0
- code_muse/stream_parser/proposed_plan_parser.py +158 -0
- code_muse/stream_parser/stream_text_chunk.py +23 -0
- code_muse/stream_parser/stream_text_parser.py +27 -0
- code_muse/stream_parser/tagged_line_parser.cpython-314-darwin.so +0 -0
- code_muse/stream_parser/tagged_line_parser.pyx +251 -0
- code_muse/stream_parser/utf8_stream_parser.cpython-314-darwin.so +0 -0
- code_muse/stream_parser/utf8_stream_parser.pyx +206 -0
- code_muse/summarization_agent.py +308 -0
- code_muse/terminal_utils.cpython-314-darwin.so +0 -0
- code_muse/terminal_utils.cpython-314-x86_64-linux-gnu.so +0 -0
- code_muse/terminal_utils.pyx +483 -0
- code_muse/tools/__init__.py +459 -0
- code_muse/tools/agent_tools.py +613 -0
- code_muse/tools/ask_user_question/__init__.py +26 -0
- code_muse/tools/ask_user_question/constants.py +73 -0
- code_muse/tools/ask_user_question/demo_tui.py +55 -0
- code_muse/tools/ask_user_question/handler.py +232 -0
- code_muse/tools/ask_user_question/models.py +302 -0
- code_muse/tools/ask_user_question/registration.py +37 -0
- code_muse/tools/ask_user_question/renderers.py +336 -0
- code_muse/tools/ask_user_question/terminal_ui.py +327 -0
- code_muse/tools/ask_user_question/theme.py +156 -0
- code_muse/tools/ask_user_question/tui_loop.py +422 -0
- code_muse/tools/background_jobs.py +99 -0
- code_muse/tools/browser/__init__.py +37 -0
- code_muse/tools/browser/browser_control.py +289 -0
- code_muse/tools/browser/browser_interactions.py +545 -0
- code_muse/tools/browser/browser_locators.py +640 -0
- code_muse/tools/browser/browser_manager.py +376 -0
- code_muse/tools/browser/browser_navigation.py +251 -0
- code_muse/tools/browser/browser_screenshot.py +180 -0
- code_muse/tools/browser/browser_scripts.py +462 -0
- code_muse/tools/browser/browser_workflows.py +222 -0
- code_muse/tools/chrome_cdp/__init__.py +1070 -0
- code_muse/tools/chrome_cdp/register_callbacks.py +61 -0
- code_muse/tools/command_runner.py +1401 -0
- code_muse/tools/common.py +1407 -0
- code_muse/tools/display.py +87 -0
- code_muse/tools/file_modifications.py +1099 -0
- code_muse/tools/file_operations.py +860 -0
- code_muse/tools/image_tools.py +185 -0
- code_muse/tools/meetin_proxy/__init__.py +243 -0
- code_muse/tools/meetin_proxy/capture_addon.py +82 -0
- code_muse/tools/meetin_proxy/proxy_manager.py +326 -0
- code_muse/tools/meetin_proxy/register_callbacks.py +45 -0
- code_muse/tools/path_policy.py +219 -0
- code_muse/tools/skills_tools.py +586 -0
- code_muse/tools/subagent_context.py +158 -0
- code_muse/tools/tools_content.py +50 -0
- code_muse/tools/universal_constructor.py +965 -0
- code_muse/uvx_detection.py +241 -0
- code_muse/version_checker.py +86 -0
- code_muse-0.0.1.data/data/code_muse/models.json +66 -0
- code_muse-0.0.1.data/data/code_muse/models_dev_api.json +1 -0
- code_muse-0.0.1.dist-info/METADATA +845 -0
- code_muse-0.0.1.dist-info/RECORD +394 -0
- code_muse-0.0.1.dist-info/WHEEL +4 -0
- code_muse-0.0.1.dist-info/entry_points.txt +2 -0
- code_muse-0.0.1.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,586 @@
|
|
|
1
|
+
"""Skills tools - dedicated tools for Agent Skills integration."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import logging
|
|
5
|
+
import shlex
|
|
6
|
+
import shutil
|
|
7
|
+
import subprocess
|
|
8
|
+
import tempfile
|
|
9
|
+
import uuid
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
from pydantic import BaseModel
|
|
13
|
+
from pydantic_ai import RunContext
|
|
14
|
+
|
|
15
|
+
from code_muse.callbacks import register_callback
|
|
16
|
+
from code_muse.config import get_global_model_name
|
|
17
|
+
from code_muse.messaging import (
|
|
18
|
+
SkillActivateMessage,
|
|
19
|
+
SkillBackgroundMessage,
|
|
20
|
+
SkillDeactivateMessage,
|
|
21
|
+
SkillEntry,
|
|
22
|
+
SkillListMessage,
|
|
23
|
+
get_message_bus,
|
|
24
|
+
)
|
|
25
|
+
from code_muse.plugins.agent_skills.register_callbacks import _deactivated_skills
|
|
26
|
+
|
|
27
|
+
logger = logging.getLogger(__name__)
|
|
28
|
+
|
|
29
|
+
# Background job store: job_id -> metadata dict
|
|
30
|
+
_background_jobs: dict[str, dict] = {}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# Output models
|
|
34
|
+
class SkillListOutput(BaseModel):
|
|
35
|
+
"""Output for list_or_search_skills tool."""
|
|
36
|
+
|
|
37
|
+
skills: list[dict] # Each has: name, description, path, tags
|
|
38
|
+
total_count: int
|
|
39
|
+
query: str | None = None # The search query if provided
|
|
40
|
+
error: str | None = None
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class SkillActivateOutput(BaseModel):
|
|
44
|
+
"""Output for activate_skill tool."""
|
|
45
|
+
|
|
46
|
+
skill_name: str
|
|
47
|
+
content: str # Full SKILL.md content
|
|
48
|
+
resources: list[str] # Available resource files
|
|
49
|
+
error: str | None = None
|
|
50
|
+
consent_required: bool = False
|
|
51
|
+
message: str = ""
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def register_activate_skill(agent):
|
|
55
|
+
"""Register the activate_skill tool."""
|
|
56
|
+
|
|
57
|
+
@agent.tool
|
|
58
|
+
async def activate_skill(
|
|
59
|
+
context: RunContext,
|
|
60
|
+
skill_name: str = "",
|
|
61
|
+
consent_confirmed: bool = False,
|
|
62
|
+
) -> SkillActivateOutput:
|
|
63
|
+
"""Activate a skill by loading its full SKILL.md instructions."""
|
|
64
|
+
# Import from plugin
|
|
65
|
+
|
|
66
|
+
from code_muse.plugins.agent_skills.config import (
|
|
67
|
+
get_skill_directories,
|
|
68
|
+
get_skills_enabled,
|
|
69
|
+
)
|
|
70
|
+
from code_muse.plugins.agent_skills.discovery import discover_skills
|
|
71
|
+
from code_muse.plugins.agent_skills.metadata import (
|
|
72
|
+
get_skill_resources,
|
|
73
|
+
load_full_skill_content,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# Check if skills enabled
|
|
77
|
+
if not get_skills_enabled():
|
|
78
|
+
return SkillActivateOutput(
|
|
79
|
+
skill_name=skill_name,
|
|
80
|
+
content="",
|
|
81
|
+
resources=[],
|
|
82
|
+
error="Skills integration is disabled. Enable it with /set skills_enabled=true",
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
# Discover skills
|
|
86
|
+
try:
|
|
87
|
+
skill_dirs = get_skill_directories()
|
|
88
|
+
discovered = discover_skills(skill_dirs)
|
|
89
|
+
except Exception as e:
|
|
90
|
+
logger.error(f"Failed to discover skills: {e}")
|
|
91
|
+
return SkillActivateOutput(
|
|
92
|
+
skill_name=skill_name,
|
|
93
|
+
content="",
|
|
94
|
+
resources=[],
|
|
95
|
+
error=f"Failed to discover skills: {e}",
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
# Find skill by name
|
|
99
|
+
skill_path = None
|
|
100
|
+
for skill_info in discovered:
|
|
101
|
+
if skill_info.name == skill_name and skill_info.has_skill_md:
|
|
102
|
+
skill_path = skill_info.path
|
|
103
|
+
break
|
|
104
|
+
|
|
105
|
+
if not skill_path:
|
|
106
|
+
return SkillActivateOutput(
|
|
107
|
+
skill_name=skill_name,
|
|
108
|
+
content="",
|
|
109
|
+
resources=[],
|
|
110
|
+
error=f"Skill '{skill_name}' not found. Use list_or_search_skills to see available skills.",
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
# Consent gating
|
|
114
|
+
if not consent_confirmed:
|
|
115
|
+
return SkillActivateOutput(
|
|
116
|
+
skill_name=skill_name,
|
|
117
|
+
content="",
|
|
118
|
+
resources=[],
|
|
119
|
+
error=None,
|
|
120
|
+
consent_required=True,
|
|
121
|
+
message=f"To activate '{skill_name}', please ask the user: 'May I activate the {skill_name} skill?' and call activate_skill again with consent_confirmed=True after they agree.",
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
# Load full content
|
|
125
|
+
content = load_full_skill_content(skill_path)
|
|
126
|
+
if content is None:
|
|
127
|
+
return SkillActivateOutput(
|
|
128
|
+
skill_name=skill_name,
|
|
129
|
+
content="",
|
|
130
|
+
resources=[],
|
|
131
|
+
error=f"Failed to load content for skill '{skill_name}'",
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
# Get resource list
|
|
135
|
+
resource_paths = get_skill_resources(skill_path)
|
|
136
|
+
resources = [str(p) for p in resource_paths]
|
|
137
|
+
|
|
138
|
+
# Emit message for UI
|
|
139
|
+
content_preview = content[:200] if content else ""
|
|
140
|
+
skill_msg = SkillActivateMessage(
|
|
141
|
+
skill_name=skill_name,
|
|
142
|
+
skill_path=str(skill_path),
|
|
143
|
+
content_preview=content_preview,
|
|
144
|
+
resource_count=len(resources),
|
|
145
|
+
success=True,
|
|
146
|
+
)
|
|
147
|
+
get_message_bus().emit(skill_msg)
|
|
148
|
+
|
|
149
|
+
return SkillActivateOutput(
|
|
150
|
+
skill_name=skill_name,
|
|
151
|
+
content=content,
|
|
152
|
+
resources=resources,
|
|
153
|
+
error=None,
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
return activate_skill
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def register_list_or_search_skills(agent):
|
|
160
|
+
"""Register the list_or_search_skills tool."""
|
|
161
|
+
|
|
162
|
+
@agent.tool
|
|
163
|
+
async def list_or_search_skills(
|
|
164
|
+
context: RunContext, query: str | None = None
|
|
165
|
+
) -> SkillListOutput:
|
|
166
|
+
"""List available skills, optionally filtered by search query.
|
|
167
|
+
|
|
168
|
+
Args:
|
|
169
|
+
query: Optional search term to filter skills by name/description/tags.
|
|
170
|
+
If None, returns all available skills.
|
|
171
|
+
"""
|
|
172
|
+
# Import from plugin
|
|
173
|
+
|
|
174
|
+
from code_muse.plugins.agent_skills.config import (
|
|
175
|
+
get_disabled_skills,
|
|
176
|
+
get_skill_directories,
|
|
177
|
+
get_skills_enabled,
|
|
178
|
+
)
|
|
179
|
+
from code_muse.plugins.agent_skills.discovery import discover_skills
|
|
180
|
+
from code_muse.plugins.agent_skills.metadata import parse_skill_metadata
|
|
181
|
+
|
|
182
|
+
# Check if skills enabled
|
|
183
|
+
if not get_skills_enabled():
|
|
184
|
+
return SkillListOutput(
|
|
185
|
+
skills=[],
|
|
186
|
+
total_count=0,
|
|
187
|
+
query=query,
|
|
188
|
+
error="Skills integration is disabled. Enable it with /set skills_enabled=true",
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
# Get disabled skills
|
|
192
|
+
disabled_skills = get_disabled_skills()
|
|
193
|
+
|
|
194
|
+
# Discover all skills
|
|
195
|
+
try:
|
|
196
|
+
skill_dirs = get_skill_directories()
|
|
197
|
+
discovered = discover_skills(skill_dirs)
|
|
198
|
+
except Exception as e:
|
|
199
|
+
logger.error(f"Failed to discover skills: {e}")
|
|
200
|
+
return SkillListOutput(
|
|
201
|
+
skills=[],
|
|
202
|
+
total_count=0,
|
|
203
|
+
query=query,
|
|
204
|
+
error=f"Failed to discover skills: {e}",
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
# Parse metadata for each skill
|
|
208
|
+
skills_list = []
|
|
209
|
+
for skill_info in discovered:
|
|
210
|
+
# Skip disabled skills
|
|
211
|
+
if skill_info.name in disabled_skills:
|
|
212
|
+
continue
|
|
213
|
+
|
|
214
|
+
# Only include skills with valid SKILL.md
|
|
215
|
+
if not skill_info.has_skill_md:
|
|
216
|
+
continue
|
|
217
|
+
|
|
218
|
+
metadata = parse_skill_metadata(skill_info.path)
|
|
219
|
+
if metadata:
|
|
220
|
+
skill_dict = {
|
|
221
|
+
"name": metadata.name,
|
|
222
|
+
"description": metadata.description,
|
|
223
|
+
"path": str(metadata.path),
|
|
224
|
+
"tags": metadata.tags,
|
|
225
|
+
"version": metadata.version,
|
|
226
|
+
"author": metadata.author,
|
|
227
|
+
"source": metadata.source or skill_info.source,
|
|
228
|
+
"trust": metadata.trust or skill_info.trust,
|
|
229
|
+
"hash": metadata.skill_md_hash or skill_info.skill_md_hash,
|
|
230
|
+
}
|
|
231
|
+
# Filter out None values for cleanliness
|
|
232
|
+
skill_dict = {k: v for k, v in skill_dict.items() if v is not None}
|
|
233
|
+
skills_list.append(skill_dict)
|
|
234
|
+
|
|
235
|
+
# Filter by query if provided
|
|
236
|
+
if query:
|
|
237
|
+
query_lower = query.lower()
|
|
238
|
+
filtered = []
|
|
239
|
+
for skill in skills_list:
|
|
240
|
+
# Check name (case-insensitive)
|
|
241
|
+
if query_lower in skill["name"].lower():
|
|
242
|
+
filtered.append(skill)
|
|
243
|
+
continue
|
|
244
|
+
|
|
245
|
+
# Check description (case-insensitive)
|
|
246
|
+
if query_lower in skill["description"].lower():
|
|
247
|
+
filtered.append(skill)
|
|
248
|
+
continue
|
|
249
|
+
|
|
250
|
+
# Check tags (case-insensitive)
|
|
251
|
+
for tag in skill["tags"]:
|
|
252
|
+
if query_lower in tag.lower():
|
|
253
|
+
filtered.append(skill)
|
|
254
|
+
break
|
|
255
|
+
skills_list = filtered
|
|
256
|
+
|
|
257
|
+
# Emit message for UI
|
|
258
|
+
skill_entries = [
|
|
259
|
+
SkillEntry(
|
|
260
|
+
name=s["name"],
|
|
261
|
+
description=s["description"],
|
|
262
|
+
path=s["path"],
|
|
263
|
+
tags=s["tags"],
|
|
264
|
+
enabled=s["name"] not in disabled_skills,
|
|
265
|
+
)
|
|
266
|
+
for s in skills_list
|
|
267
|
+
]
|
|
268
|
+
skill_msg = SkillListMessage(
|
|
269
|
+
skills=skill_entries,
|
|
270
|
+
query=query,
|
|
271
|
+
total_count=len(skills_list),
|
|
272
|
+
)
|
|
273
|
+
get_message_bus().emit(skill_msg)
|
|
274
|
+
|
|
275
|
+
return SkillListOutput(
|
|
276
|
+
skills=skills_list, total_count=len(skills_list), query=query, error=None
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
return list_or_search_skills
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
# Output models for new tools
|
|
283
|
+
class SkillDeactivateOutput(BaseModel):
|
|
284
|
+
"""Output for deactivate_skill tool."""
|
|
285
|
+
|
|
286
|
+
skill_name: str
|
|
287
|
+
success: bool
|
|
288
|
+
message: str
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
class SkillBackgroundOutput(BaseModel):
|
|
292
|
+
"""Output for background skill job tools."""
|
|
293
|
+
|
|
294
|
+
job_id: str
|
|
295
|
+
skill_name: str
|
|
296
|
+
status: str # "started" | "running" | "complete" | "error"
|
|
297
|
+
result: str = ""
|
|
298
|
+
log_file: str = ""
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def register_deactivate_skill(agent):
|
|
302
|
+
"""Register the deactivate_skill tool."""
|
|
303
|
+
|
|
304
|
+
@agent.tool
|
|
305
|
+
async def deactivate_skill(
|
|
306
|
+
context: RunContext, skill_name: str = ""
|
|
307
|
+
) -> SkillDeactivateOutput:
|
|
308
|
+
"""Deactivate a skill so it is excluded from prompt injection."""
|
|
309
|
+
_deactivated_skills.add(skill_name)
|
|
310
|
+
message = f"Skill '{skill_name}' has been deactivated for this session."
|
|
311
|
+
skill_msg = SkillDeactivateMessage(
|
|
312
|
+
skill_name=skill_name, success=True, message=message
|
|
313
|
+
)
|
|
314
|
+
get_message_bus().emit(skill_msg)
|
|
315
|
+
return SkillDeactivateOutput(
|
|
316
|
+
skill_name=skill_name, success=True, message=message
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
return deactivate_skill
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def register_run_skill_background(agent):
|
|
323
|
+
"""Register the run_skill_background tool."""
|
|
324
|
+
|
|
325
|
+
@agent.tool
|
|
326
|
+
async def run_skill_background(
|
|
327
|
+
context: RunContext, skill_name: str = "", task: str = ""
|
|
328
|
+
) -> SkillBackgroundOutput:
|
|
329
|
+
"""Launch a headless background agent with a skill as system prompt."""
|
|
330
|
+
from code_muse.plugins.agent_skills.config import (
|
|
331
|
+
get_skill_directories,
|
|
332
|
+
get_skills_enabled,
|
|
333
|
+
)
|
|
334
|
+
from code_muse.plugins.agent_skills.discovery import discover_skills
|
|
335
|
+
from code_muse.plugins.agent_skills.metadata import (
|
|
336
|
+
load_full_skill_content,
|
|
337
|
+
parse_yaml_frontmatter,
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
if not get_skills_enabled():
|
|
341
|
+
return SkillBackgroundOutput(
|
|
342
|
+
job_id="",
|
|
343
|
+
skill_name=skill_name,
|
|
344
|
+
status="error",
|
|
345
|
+
result="Skills integration is disabled.",
|
|
346
|
+
log_file="",
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
try:
|
|
350
|
+
skill_dirs = get_skill_directories()
|
|
351
|
+
discovered = discover_skills(skill_dirs)
|
|
352
|
+
except Exception as e:
|
|
353
|
+
logger.error(f"Failed to discover skills: {e}")
|
|
354
|
+
return SkillBackgroundOutput(
|
|
355
|
+
job_id="",
|
|
356
|
+
skill_name=skill_name,
|
|
357
|
+
status="error",
|
|
358
|
+
result=f"Failed to discover skills: {e}",
|
|
359
|
+
log_file="",
|
|
360
|
+
)
|
|
361
|
+
|
|
362
|
+
skill_path = None
|
|
363
|
+
for skill_info in discovered:
|
|
364
|
+
if skill_info.name == skill_name and skill_info.has_skill_md:
|
|
365
|
+
skill_path = skill_info.path
|
|
366
|
+
break
|
|
367
|
+
|
|
368
|
+
if not skill_path:
|
|
369
|
+
return SkillBackgroundOutput(
|
|
370
|
+
job_id="",
|
|
371
|
+
skill_name=skill_name,
|
|
372
|
+
status="error",
|
|
373
|
+
result=f"Skill '{skill_name}' not found.",
|
|
374
|
+
log_file="",
|
|
375
|
+
)
|
|
376
|
+
|
|
377
|
+
content = load_full_skill_content(skill_path)
|
|
378
|
+
if content is None:
|
|
379
|
+
return SkillBackgroundOutput(
|
|
380
|
+
job_id="",
|
|
381
|
+
skill_name=skill_name,
|
|
382
|
+
status="error",
|
|
383
|
+
result=f"Failed to load content for skill '{skill_name}'.",
|
|
384
|
+
log_file="",
|
|
385
|
+
)
|
|
386
|
+
|
|
387
|
+
# Parse frontmatter for worktree flag
|
|
388
|
+
frontmatter = parse_yaml_frontmatter(content)
|
|
389
|
+
use_worktree = bool(frontmatter and frontmatter.get("worktree"))
|
|
390
|
+
|
|
391
|
+
# Prepare ephemeral worktree / cwd
|
|
392
|
+
if use_worktree:
|
|
393
|
+
worktree_dir = tempfile.mkdtemp(prefix=f"skill_bg_{skill_name}_")
|
|
394
|
+
# Attempt git worktree if inside a git repo
|
|
395
|
+
try:
|
|
396
|
+
git_proc = await asyncio.create_subprocess_exec(
|
|
397
|
+
"git",
|
|
398
|
+
"rev-parse",
|
|
399
|
+
"--show-toplevel",
|
|
400
|
+
stdout=asyncio.subprocess.PIPE,
|
|
401
|
+
stderr=asyncio.subprocess.PIPE,
|
|
402
|
+
)
|
|
403
|
+
git_stdout, _ = await git_proc.communicate()
|
|
404
|
+
if git_proc.returncode != 0:
|
|
405
|
+
raise subprocess.CalledProcessError(
|
|
406
|
+
git_proc.returncode, ["git", "rev-parse", "--show-toplevel"]
|
|
407
|
+
)
|
|
408
|
+
git_root = git_stdout.decode().strip()
|
|
409
|
+
branch_name = f"skill-bg-{uuid.uuid4().hex[:8]}"
|
|
410
|
+
worktree_proc = await asyncio.create_subprocess_exec(
|
|
411
|
+
"git",
|
|
412
|
+
"worktree",
|
|
413
|
+
"add",
|
|
414
|
+
worktree_dir,
|
|
415
|
+
"-b",
|
|
416
|
+
branch_name,
|
|
417
|
+
cwd=git_root,
|
|
418
|
+
stdout=asyncio.subprocess.PIPE,
|
|
419
|
+
stderr=asyncio.subprocess.PIPE,
|
|
420
|
+
)
|
|
421
|
+
await worktree_proc.wait()
|
|
422
|
+
if worktree_proc.returncode != 0:
|
|
423
|
+
raise subprocess.CalledProcessError(
|
|
424
|
+
worktree_proc.returncode,
|
|
425
|
+
["git", "worktree", "add", worktree_dir, "-b", branch_name],
|
|
426
|
+
)
|
|
427
|
+
except Exception:
|
|
428
|
+
# Fallback: just use the temp dir as plain cwd
|
|
429
|
+
pass
|
|
430
|
+
cwd = worktree_dir
|
|
431
|
+
else:
|
|
432
|
+
cwd = Path.cwd()
|
|
433
|
+
|
|
434
|
+
# Write skill content and task to temp files
|
|
435
|
+
skill_content_file = tempfile.mktemp(
|
|
436
|
+
prefix=f"skill_{skill_name}_", suffix=".md"
|
|
437
|
+
)
|
|
438
|
+
Path(skill_content_file).write_text(content, encoding="utf-8")
|
|
439
|
+
|
|
440
|
+
task_file = tempfile.mktemp(prefix="skill_task_", suffix=".txt")
|
|
441
|
+
Path(task_file).write_text(task, encoding="utf-8")
|
|
442
|
+
|
|
443
|
+
job_id = uuid.uuid4().hex[:8]
|
|
444
|
+
# TODO: PEP 750 t-string — use templatelib when stable
|
|
445
|
+
log_file = f"/tmp/skill_bg_{job_id}.log"
|
|
446
|
+
|
|
447
|
+
# Build and launch command
|
|
448
|
+
model = get_global_model_name() or "gemini"
|
|
449
|
+
cmd = (
|
|
450
|
+
# TODO: PEP 750 t-string — use templatelib when stable
|
|
451
|
+
f"cat {shlex.quote(task_file)} | code-muse --headless "
|
|
452
|
+
# TODO: PEP 750 t-string — use templatelib when stable
|
|
453
|
+
f"--system-prompt {shlex.quote(skill_content_file)} "
|
|
454
|
+
# TODO: PEP 750 t-string — use templatelib when stable
|
|
455
|
+
f"--model {shlex.quote(model)} --cwd {shlex.quote(str(cwd))} "
|
|
456
|
+
# TODO: PEP 750 t-string — use templatelib when stable
|
|
457
|
+
f"> {shlex.quote(log_file)} 2>&1"
|
|
458
|
+
)
|
|
459
|
+
|
|
460
|
+
try:
|
|
461
|
+
process = subprocess.Popen(cmd, shell=True, cwd=cwd)
|
|
462
|
+
except Exception as e:
|
|
463
|
+
return SkillBackgroundOutput(
|
|
464
|
+
job_id=job_id,
|
|
465
|
+
skill_name=skill_name,
|
|
466
|
+
status="error",
|
|
467
|
+
result=f"Failed to start background job: {e}",
|
|
468
|
+
log_file=log_file,
|
|
469
|
+
)
|
|
470
|
+
|
|
471
|
+
_background_jobs[job_id] = {
|
|
472
|
+
"process": process,
|
|
473
|
+
"log_file": log_file,
|
|
474
|
+
"skill_name": skill_name,
|
|
475
|
+
"task": task,
|
|
476
|
+
"skill_content_file": skill_content_file,
|
|
477
|
+
"task_file": task_file,
|
|
478
|
+
"cwd": cwd,
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
bus_msg = SkillBackgroundMessage(
|
|
482
|
+
job_id=job_id,
|
|
483
|
+
skill_name=skill_name,
|
|
484
|
+
status="started",
|
|
485
|
+
result="",
|
|
486
|
+
log_file=log_file,
|
|
487
|
+
)
|
|
488
|
+
get_message_bus().emit(bus_msg)
|
|
489
|
+
|
|
490
|
+
return SkillBackgroundOutput(
|
|
491
|
+
job_id=job_id,
|
|
492
|
+
skill_name=skill_name,
|
|
493
|
+
status="started",
|
|
494
|
+
result="Background job started.",
|
|
495
|
+
log_file=log_file,
|
|
496
|
+
)
|
|
497
|
+
|
|
498
|
+
return run_skill_background
|
|
499
|
+
|
|
500
|
+
|
|
501
|
+
def register_check_skill_background(agent):
|
|
502
|
+
"""Register the check_skill_background tool."""
|
|
503
|
+
|
|
504
|
+
@agent.tool
|
|
505
|
+
async def check_skill_background(
|
|
506
|
+
context: RunContext, job_id: str = ""
|
|
507
|
+
) -> SkillBackgroundOutput:
|
|
508
|
+
"""Check the status of a background skill job."""
|
|
509
|
+
job = _background_jobs.get(job_id)
|
|
510
|
+
if not job:
|
|
511
|
+
return SkillBackgroundOutput(
|
|
512
|
+
job_id=job_id,
|
|
513
|
+
skill_name="",
|
|
514
|
+
status="error",
|
|
515
|
+
result=f"Job '{job_id}' not found.",
|
|
516
|
+
log_file="",
|
|
517
|
+
)
|
|
518
|
+
|
|
519
|
+
process = job["process"]
|
|
520
|
+
log_file = job["log_file"]
|
|
521
|
+
skill_name = job["skill_name"]
|
|
522
|
+
|
|
523
|
+
poll_result = process.poll()
|
|
524
|
+
if poll_result is None:
|
|
525
|
+
return SkillBackgroundOutput(
|
|
526
|
+
job_id=job_id,
|
|
527
|
+
skill_name=skill_name,
|
|
528
|
+
status="running",
|
|
529
|
+
result="Job is still running.",
|
|
530
|
+
log_file=log_file,
|
|
531
|
+
)
|
|
532
|
+
|
|
533
|
+
# Process finished – read log
|
|
534
|
+
try:
|
|
535
|
+
result_text = Path(log_file).read_text(encoding="utf-8")
|
|
536
|
+
except Exception:
|
|
537
|
+
result_text = ""
|
|
538
|
+
|
|
539
|
+
status = "complete" if poll_result == 0 else "error"
|
|
540
|
+
return SkillBackgroundOutput(
|
|
541
|
+
job_id=job_id,
|
|
542
|
+
skill_name=skill_name,
|
|
543
|
+
status=status,
|
|
544
|
+
result=result_text,
|
|
545
|
+
log_file=log_file,
|
|
546
|
+
)
|
|
547
|
+
|
|
548
|
+
return check_skill_background
|
|
549
|
+
|
|
550
|
+
|
|
551
|
+
def _cleanup_skill_temp_files() -> None:
|
|
552
|
+
"""Clean up background skill jobs and temp files on shutdown."""
|
|
553
|
+
count = len(_background_jobs)
|
|
554
|
+
if not count:
|
|
555
|
+
return
|
|
556
|
+
|
|
557
|
+
for _job_id, job in list(_background_jobs.items()):
|
|
558
|
+
process = job.get("process")
|
|
559
|
+
if process and process.poll() is None:
|
|
560
|
+
try:
|
|
561
|
+
process.terminate()
|
|
562
|
+
process.wait(timeout=5)
|
|
563
|
+
except Exception:
|
|
564
|
+
try:
|
|
565
|
+
process.kill()
|
|
566
|
+
process.wait(timeout=2)
|
|
567
|
+
except Exception:
|
|
568
|
+
pass
|
|
569
|
+
|
|
570
|
+
cwd = job.get("cwd")
|
|
571
|
+
if cwd and "skill_bg_" in str(cwd):
|
|
572
|
+
shutil.rmtree(cwd, ignore_errors=True)
|
|
573
|
+
|
|
574
|
+
skill_content_file = job.get("skill_content_file")
|
|
575
|
+
if skill_content_file and Path(skill_content_file).exists():
|
|
576
|
+
Path(skill_content_file).unlink(missing_ok=True)
|
|
577
|
+
|
|
578
|
+
task_file = job.get("task_file")
|
|
579
|
+
if task_file and Path(task_file).exists():
|
|
580
|
+
Path(task_file).unlink(missing_ok=True)
|
|
581
|
+
|
|
582
|
+
_background_jobs.clear()
|
|
583
|
+
logger.info("Cleaned up %s background skill job(s)", count)
|
|
584
|
+
|
|
585
|
+
|
|
586
|
+
register_callback("shutdown", _cleanup_skill_temp_files)
|