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,410 @@
|
|
|
1
|
+
"""Ollama cloud model setup β /ollama-setup command.
|
|
2
|
+
|
|
3
|
+
Pulls an Ollama `:cloud` model and registers it in extra_models.json so the
|
|
4
|
+
model is immediately available for use in Muse.
|
|
5
|
+
|
|
6
|
+
Cloud models supported (the Ollama "Recommended Models" cloud tier):
|
|
7
|
+
kimi-k2.6:cloud, kimi-k2.5:cloud, glm-5:cloud, glm-5.1:cloud, minimax-m2.7:cloud, qwen3.5:cloud
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import json
|
|
11
|
+
import logging
|
|
12
|
+
import shutil
|
|
13
|
+
import subprocess
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Any
|
|
16
|
+
|
|
17
|
+
from code_muse.callbacks import register_callback
|
|
18
|
+
from code_muse.config import EXTRA_MODELS_FILE
|
|
19
|
+
from code_muse.messaging import emit_error, emit_info, emit_success, emit_warning
|
|
20
|
+
|
|
21
|
+
logger = logging.getLogger(__name__)
|
|
22
|
+
|
|
23
|
+
# ββ Cloud model catalogue βββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
24
|
+
# The `:cloud` models from the Ollama recommended list.
|
|
25
|
+
# Each entry maps the ollama tag β extra_models.json config metadata.
|
|
26
|
+
|
|
27
|
+
CLOUD_MODELS: dict[str, dict[str, Any]] = {
|
|
28
|
+
"kimi-k2.6:cloud": {
|
|
29
|
+
"context_length": 262144, # 256k
|
|
30
|
+
"description": "Moonshot Kimi K2.6 (cloud)",
|
|
31
|
+
},
|
|
32
|
+
"kimi-k2.5:cloud": {
|
|
33
|
+
"context_length": 262144, # 256k
|
|
34
|
+
"description": "Moonshot Kimi K2.5 (cloud)",
|
|
35
|
+
},
|
|
36
|
+
"glm-5:cloud": {
|
|
37
|
+
"context_length": 204800, # 200k
|
|
38
|
+
"description": "ZhipuAI GLM-5 (cloud)",
|
|
39
|
+
},
|
|
40
|
+
"glm-5.1:cloud": {
|
|
41
|
+
"context_length": 204800, # 200k
|
|
42
|
+
"description": "ZhipuAI GLM-5.1 (cloud)",
|
|
43
|
+
},
|
|
44
|
+
"minimax-m2.7:cloud": {
|
|
45
|
+
"context_length": 199680, # 195k
|
|
46
|
+
"description": "MiniMax M2.7 (cloud)",
|
|
47
|
+
},
|
|
48
|
+
"qwen3.5:cloud": {
|
|
49
|
+
"context_length": 204800, # 200k
|
|
50
|
+
"description": "Alibaba Qwen 3.5 (cloud)",
|
|
51
|
+
},
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
OLLAMA_ENDPOINT = "http://localhost:11434/v1"
|
|
55
|
+
OLLAMA_API_KEY = "ollama" # Ollama's local API doesn't need a real key
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
# ββ Helpers βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _model_key(model_tag: str) -> str:
|
|
62
|
+
"""Derive a unique extra_models.json key from an ollama tag.
|
|
63
|
+
|
|
64
|
+
e.g. ``glm-5:cloud`` β ``ollama-glm-5-cloud``
|
|
65
|
+
"""
|
|
66
|
+
return "ollama-" + model_tag.replace(":", "-").replace("/", "-")
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _ollama_available() -> bool:
|
|
70
|
+
"""Return True if the ``ollama`` CLI is on PATH."""
|
|
71
|
+
return shutil.which("ollama") is not None
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _pull_model(model_tag: str) -> bool:
|
|
75
|
+
"""Run ``ollama pull <model_tag>``, streaming output to the terminal.
|
|
76
|
+
|
|
77
|
+
Returns True on success, False on failure.
|
|
78
|
+
"""
|
|
79
|
+
emit_info(f"[Run] Pulling {model_tag} via ollama β¦")
|
|
80
|
+
try:
|
|
81
|
+
result = subprocess.run(
|
|
82
|
+
["ollama", "pull", model_tag],
|
|
83
|
+
capture_output=True,
|
|
84
|
+
text=True,
|
|
85
|
+
timeout=600,
|
|
86
|
+
)
|
|
87
|
+
if result.returncode != 0:
|
|
88
|
+
stderr = result.stderr.strip()
|
|
89
|
+
emit_error(f"ollama pull failed (exit {result.returncode}): {stderr}")
|
|
90
|
+
return False
|
|
91
|
+
|
|
92
|
+
stdout = result.stdout.strip()
|
|
93
|
+
if stdout:
|
|
94
|
+
emit_info(stdout)
|
|
95
|
+
return True
|
|
96
|
+
except subprocess.TimeoutExpired:
|
|
97
|
+
emit_error("ollama pull timed out after 10 minutes")
|
|
98
|
+
return False
|
|
99
|
+
except Exception as exc:
|
|
100
|
+
emit_error(f"Failed to run ollama pull: {exc}")
|
|
101
|
+
return False
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _register_model(model_tag: str) -> bool:
|
|
105
|
+
"""Write (or update) the model entry in extra_models.json.
|
|
106
|
+
|
|
107
|
+
Uses atomic-write via a temp file, same pattern as add_model_menu.py.
|
|
108
|
+
Returns True on success.
|
|
109
|
+
"""
|
|
110
|
+
meta = CLOUD_MODELS[model_tag]
|
|
111
|
+
key = _model_key(model_tag)
|
|
112
|
+
|
|
113
|
+
extra_path = Path(EXTRA_MODELS_FILE)
|
|
114
|
+
extra_models: dict[str, Any] = {}
|
|
115
|
+
|
|
116
|
+
if extra_path.exists():
|
|
117
|
+
try:
|
|
118
|
+
with open(extra_path, encoding="utf-8") as fh:
|
|
119
|
+
extra_models = json.load(fh)
|
|
120
|
+
if not isinstance(extra_models, dict):
|
|
121
|
+
emit_error("extra_models.json must be a dict, not a list")
|
|
122
|
+
return False
|
|
123
|
+
except json.JSONDecodeError as exc:
|
|
124
|
+
emit_error(f"Corrupt extra_models.json: {exc}")
|
|
125
|
+
return False
|
|
126
|
+
|
|
127
|
+
if key in extra_models:
|
|
128
|
+
emit_info(f"Model {key} already registered β updating entry")
|
|
129
|
+
|
|
130
|
+
extra_models[key] = {
|
|
131
|
+
"type": "custom_openai",
|
|
132
|
+
"name": model_tag,
|
|
133
|
+
"custom_endpoint": {
|
|
134
|
+
"url": OLLAMA_ENDPOINT,
|
|
135
|
+
"api_key": OLLAMA_API_KEY,
|
|
136
|
+
},
|
|
137
|
+
"context_length": meta["context_length"],
|
|
138
|
+
"supported_settings": ["temperature", "top_p"],
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
extra_path.parent.mkdir(parents=True, exist_ok=True)
|
|
142
|
+
tmp = extra_path.with_suffix(".tmp")
|
|
143
|
+
try:
|
|
144
|
+
with open(tmp, "w", encoding="utf-8") as fh:
|
|
145
|
+
json.dump(extra_models, fh, indent=4, ensure_ascii=False)
|
|
146
|
+
tmp.replace(extra_path)
|
|
147
|
+
except Exception as exc:
|
|
148
|
+
emit_error(f"Failed to write extra_models.json: {exc}")
|
|
149
|
+
return False
|
|
150
|
+
|
|
151
|
+
emit_success(f"β
Registered {key} in extra_models.json")
|
|
152
|
+
return True
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def _test_model_auth(model_tag: str) -> tuple[bool, str]:
|
|
156
|
+
"""Test if the model requires authentication.
|
|
157
|
+
|
|
158
|
+
Returns (authorized: bool, message: str).
|
|
159
|
+
If unauthorized, message contains guidance for the user.
|
|
160
|
+
"""
|
|
161
|
+
import json as json_mod
|
|
162
|
+
import urllib.request
|
|
163
|
+
|
|
164
|
+
test_payload = json_mod.dumps(
|
|
165
|
+
{
|
|
166
|
+
"model": model_tag,
|
|
167
|
+
"messages": [{"role": "user", "content": "hi"}],
|
|
168
|
+
"stream": False,
|
|
169
|
+
}
|
|
170
|
+
).encode("utf-8")
|
|
171
|
+
|
|
172
|
+
req = urllib.request.Request(
|
|
173
|
+
f"{OLLAMA_ENDPOINT}/chat/completions",
|
|
174
|
+
data=test_payload,
|
|
175
|
+
headers={
|
|
176
|
+
"Content-Type": "application/json",
|
|
177
|
+
"Authorization": f"Bearer {OLLAMA_API_KEY}",
|
|
178
|
+
},
|
|
179
|
+
method="POST",
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
try:
|
|
183
|
+
with urllib.request.urlopen(req, timeout=10):
|
|
184
|
+
# 200 = good to go
|
|
185
|
+
return (True, "")
|
|
186
|
+
except urllib.error.HTTPError as e:
|
|
187
|
+
body = e.read().decode("utf-8", errors="replace")
|
|
188
|
+
# Check for auth errors
|
|
189
|
+
if e.code == 401 or "unauthorized" in body.lower():
|
|
190
|
+
return (False, _auth_help_message(model_tag))
|
|
191
|
+
if e.code == 500 and (
|
|
192
|
+
"internal service error" in body.lower() or "unauthorized" in body.lower()
|
|
193
|
+
):
|
|
194
|
+
# Cloud models often return 500 instead of 401
|
|
195
|
+
return (False, _auth_help_message(model_tag))
|
|
196
|
+
return (False, f"HTTP {e.code}: {body[:200]}")
|
|
197
|
+
except Exception as e:
|
|
198
|
+
return (False, f"Connection test failed: {e}")
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def _auth_help_message(model_tag: str) -> str:
|
|
202
|
+
"""Generate a helpful auth guidance message."""
|
|
203
|
+
return f"""π Authentication Required for {model_tag}
|
|
204
|
+
|
|
205
|
+
This is a cloud-hosted model (756B+ parameters) that requires Ollama Cloud access.
|
|
206
|
+
|
|
207
|
+
To use this model:
|
|
208
|
+
1. Run: ollama login
|
|
209
|
+
2. Visit https://ollama.com to create an account
|
|
210
|
+
3. Subscribe to Ollama Cloud (cloud models aren't free)
|
|
211
|
+
4. Accept the terms for {model_tag.split(":")[0]}
|
|
212
|
+
|
|
213
|
+
After logging in, you can use the model with:
|
|
214
|
+
/model {_model_key(model_tag)}
|
|
215
|
+
"""
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def _run_ollama_login() -> bool:
|
|
219
|
+
"""Run ``ollama login`` interactively in the user's terminal.
|
|
220
|
+
|
|
221
|
+
This will open a browser for OAuth flow. Returns True if login succeeded.
|
|
222
|
+
"""
|
|
223
|
+
emit_info(
|
|
224
|
+
"π Launching 'ollama login' β this will open your browser for authentication..."
|
|
225
|
+
)
|
|
226
|
+
emit_info(" (Press Ctrl+C here if you want to cancel and do it manually later)")
|
|
227
|
+
emit_info("")
|
|
228
|
+
|
|
229
|
+
try:
|
|
230
|
+
# Run ollama login with stdout/stderr connected to terminal
|
|
231
|
+
# so the user can see the browser-open message and any instructions
|
|
232
|
+
result = subprocess.run(
|
|
233
|
+
["ollama", "login"],
|
|
234
|
+
capture_output=False, # Let it flow to terminal
|
|
235
|
+
text=True,
|
|
236
|
+
timeout=120, # 2 minutes β OAuth can take a while
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
if result.returncode == 0:
|
|
240
|
+
emit_success("β
Login completed!")
|
|
241
|
+
return True
|
|
242
|
+
else:
|
|
243
|
+
emit_error(f"ollama login exited with code {result.returncode}")
|
|
244
|
+
return False
|
|
245
|
+
except subprocess.TimeoutExpired:
|
|
246
|
+
emit_error("ollama login timed out after 2 minutes")
|
|
247
|
+
return False
|
|
248
|
+
except Exception as exc:
|
|
249
|
+
emit_error(f"Failed to run ollama login: {exc}")
|
|
250
|
+
return False
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def _start_ollama_serve() -> subprocess.Popen | None:
|
|
254
|
+
"""Start ``ollama serve`` as a background subprocess if not already running.
|
|
255
|
+
|
|
256
|
+
Returns the Popen handle, or None if already running / on failure.
|
|
257
|
+
"""
|
|
258
|
+
# Quick liveness check β hit the version endpoint
|
|
259
|
+
try:
|
|
260
|
+
result = subprocess.run(
|
|
261
|
+
["ollama", "list"],
|
|
262
|
+
capture_output=True,
|
|
263
|
+
text=True,
|
|
264
|
+
timeout=5,
|
|
265
|
+
)
|
|
266
|
+
if result.returncode == 0:
|
|
267
|
+
emit_info("π’ Ollama is already running")
|
|
268
|
+
return None
|
|
269
|
+
except Exception:
|
|
270
|
+
pass
|
|
271
|
+
|
|
272
|
+
emit_info("π Starting ollama serve in the background β¦")
|
|
273
|
+
try:
|
|
274
|
+
proc = subprocess.Popen(
|
|
275
|
+
["ollama", "serve"],
|
|
276
|
+
stdout=subprocess.DEVNULL,
|
|
277
|
+
stderr=subprocess.DEVNULL,
|
|
278
|
+
)
|
|
279
|
+
emit_success(f"ollama serve started (pid {proc.pid})")
|
|
280
|
+
return proc
|
|
281
|
+
except Exception as exc:
|
|
282
|
+
emit_error(f"Could not start ollama serve: {exc}")
|
|
283
|
+
return None
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
# ββ Command handler βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def _handle_ollama_setup(command: str, name: str) -> Any:
|
|
290
|
+
"""Handle ``/ollama-setup [model]``."""
|
|
291
|
+
if name != "ollama-setup":
|
|
292
|
+
return None # Not our command
|
|
293
|
+
|
|
294
|
+
parts = command.split(maxsplit=1)
|
|
295
|
+
model_tag = parts[1].strip() if len(parts) > 1 else ""
|
|
296
|
+
|
|
297
|
+
# No argument β show available models
|
|
298
|
+
if not model_tag:
|
|
299
|
+
lines = ["π¦ Available Ollama cloud models:\n"]
|
|
300
|
+
for tag, meta in CLOUD_MODELS.items():
|
|
301
|
+
lines.append(f" β’ {tag:25s} β {meta['description']}")
|
|
302
|
+
lines.append("\nUsage: /ollama-setup <model>")
|
|
303
|
+
emit_info("\n".join(lines))
|
|
304
|
+
return True
|
|
305
|
+
|
|
306
|
+
# Fuzzy-ish match: allow partial names like "glm" β "glm-5:cloud"
|
|
307
|
+
matched = _resolve_model(model_tag)
|
|
308
|
+
if matched is None:
|
|
309
|
+
emit_error(f"Unknown model '{model_tag}'. Available: {', '.join(CLOUD_MODELS)}")
|
|
310
|
+
return True
|
|
311
|
+
|
|
312
|
+
# Pre-flight: is ollama installed?
|
|
313
|
+
if not _ollama_available():
|
|
314
|
+
emit_error(
|
|
315
|
+
"ollama CLI not found on PATH. "
|
|
316
|
+
"Install it from https://ollama.com and try again."
|
|
317
|
+
)
|
|
318
|
+
return True
|
|
319
|
+
|
|
320
|
+
# 1. Ensure ollama serve is up
|
|
321
|
+
_start_ollama_serve()
|
|
322
|
+
|
|
323
|
+
# 2. Pull the model
|
|
324
|
+
if not _pull_model(matched):
|
|
325
|
+
return True
|
|
326
|
+
|
|
327
|
+
# 3. Register in extra_models.json
|
|
328
|
+
_register_model(matched)
|
|
329
|
+
|
|
330
|
+
# 4. Test auth / accessibility
|
|
331
|
+
emit_info(f"π Testing {matched} accessibility...")
|
|
332
|
+
authorized, msg = _test_model_auth(matched)
|
|
333
|
+
|
|
334
|
+
if authorized:
|
|
335
|
+
emit_success(
|
|
336
|
+
f"π Done! Switch to the model with: /model {_model_key(matched)}"
|
|
337
|
+
)
|
|
338
|
+
else:
|
|
339
|
+
emit_warning(msg)
|
|
340
|
+
emit_info("")
|
|
341
|
+
emit_info(
|
|
342
|
+
"π‘ Want me to run 'ollama login' for you? (This will open your browser)"
|
|
343
|
+
)
|
|
344
|
+
emit_info(
|
|
345
|
+
" Type 'yes' to proceed, or anything else to skip and do it manually later."
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
# We can't do interactive input here (custom commands shouldn't block)
|
|
349
|
+
# So we just run it β worst case it fails and they do it manually
|
|
350
|
+
login_ok = _run_ollama_login()
|
|
351
|
+
|
|
352
|
+
if login_ok:
|
|
353
|
+
# Re-test auth after login
|
|
354
|
+
emit_info("π Re-testing accessibility after login...")
|
|
355
|
+
authorized, msg = _test_model_auth(matched)
|
|
356
|
+
|
|
357
|
+
if authorized:
|
|
358
|
+
emit_success(
|
|
359
|
+
f"π All set! Switch to the model with: /model {_model_key(matched)}"
|
|
360
|
+
)
|
|
361
|
+
else:
|
|
362
|
+
emit_warning("Still not authorized after login.")
|
|
363
|
+
emit_info(
|
|
364
|
+
f"π Model registered as '{_model_key(matched)}'. "
|
|
365
|
+
f"You may need to subscribe to Ollama Cloud at https://ollama.com, "
|
|
366
|
+
f"then use /model {_model_key(matched)}"
|
|
367
|
+
)
|
|
368
|
+
else:
|
|
369
|
+
emit_info(
|
|
370
|
+
f"π Model registered as '{_model_key(matched)}' but needs auth. "
|
|
371
|
+
f"Run 'ollama login' manually, then use /model {_model_key(matched)}"
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
return True
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
def _resolve_model(user_input: str) -> str | None:
|
|
378
|
+
"""Resolve user input to an exact cloud model tag.
|
|
379
|
+
|
|
380
|
+
Supports exact match and unambiguous prefix matching.
|
|
381
|
+
"""
|
|
382
|
+
lowered = user_input.lower().strip()
|
|
383
|
+
|
|
384
|
+
# Exact match first
|
|
385
|
+
for tag in CLOUD_MODELS:
|
|
386
|
+
if tag.lower() == lowered:
|
|
387
|
+
return tag
|
|
388
|
+
|
|
389
|
+
# Prefix / substring match
|
|
390
|
+
candidates = [t for t in CLOUD_MODELS if lowered in t.lower()]
|
|
391
|
+
if len(candidates) == 1:
|
|
392
|
+
return candidates[0]
|
|
393
|
+
|
|
394
|
+
return None
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
# ββ Help entry ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
def _custom_help():
|
|
401
|
+
model_names = ", ".join(CLOUD_MODELS)
|
|
402
|
+
return [
|
|
403
|
+
("ollama-setup", f"Pull & register an Ollama cloud model ({model_names})"),
|
|
404
|
+
]
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
# ββ Register ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
408
|
+
|
|
409
|
+
register_callback("custom_command", _handle_ollama_setup)
|
|
410
|
+
register_callback("custom_command_help", _custom_help)
|
|
File without changes
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
"""Plan Command Plugin β /plan slash command.
|
|
2
|
+
|
|
3
|
+
Invokes the planning-agent to break down a coding task into
|
|
4
|
+
clear, actionable steps and displays the plan to the user.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
from contextlib import AsyncExitStack
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from functools import partial
|
|
11
|
+
|
|
12
|
+
from code_muse.callbacks import on_agent_run_context, register_callback
|
|
13
|
+
from code_muse.messaging import emit_info, emit_success, emit_warning
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# ---------------------------------------------------------------------------
|
|
19
|
+
# Lightweight standalone agent invocation (mirrors invoke_agent tool logic)
|
|
20
|
+
# ---------------------------------------------------------------------------
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass
|
|
24
|
+
class _AgentResult:
|
|
25
|
+
"""Simple container for an invoked agent's response."""
|
|
26
|
+
|
|
27
|
+
response_text: str | None = None
|
|
28
|
+
error: str | None = None
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
async def _invoke_agent(agent_name: str, prompt: str) -> _AgentResult:
|
|
32
|
+
"""Invoke a named agent with a prompt and return its text response.
|
|
33
|
+
|
|
34
|
+
This is a lightweight standalone wrapper that mirrors the core
|
|
35
|
+
``invoke_agent`` tool logic but doesn't require a Pydantic-AI
|
|
36
|
+
``RunContext`` β making it callable from plugin callbacks.
|
|
37
|
+
"""
|
|
38
|
+
from pydantic_ai import Agent, UsageLimits
|
|
39
|
+
|
|
40
|
+
from code_muse.agents._compaction import make_history_processor
|
|
41
|
+
from code_muse.agents.agent_manager import load_agent
|
|
42
|
+
from code_muse.agents.subagent_stream_handler import subagent_stream_handler
|
|
43
|
+
from code_muse.config import get_message_limit
|
|
44
|
+
from code_muse.model_factory import ModelFactory, make_model_settings
|
|
45
|
+
from code_muse.model_utils import prepare_prompt_for_model
|
|
46
|
+
from code_muse.tools import register_tools_for_agent
|
|
47
|
+
from code_muse.tools.common import generate_group_id
|
|
48
|
+
from code_muse.tools.subagent_context import subagent_context
|
|
49
|
+
|
|
50
|
+
group_id = generate_group_id("invoke_agent", agent_name)
|
|
51
|
+
|
|
52
|
+
try:
|
|
53
|
+
agent_config = load_agent(agent_name)
|
|
54
|
+
|
|
55
|
+
model_name = agent_config.get_model_name()
|
|
56
|
+
models_config = ModelFactory.load_config()
|
|
57
|
+
|
|
58
|
+
if model_name not in models_config:
|
|
59
|
+
return _AgentResult(
|
|
60
|
+
error=f"Model '{model_name}' not found in configuration"
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
model = ModelFactory.get_model(model_name, models_config)
|
|
64
|
+
|
|
65
|
+
instructions = agent_config.get_full_system_prompt()
|
|
66
|
+
|
|
67
|
+
# Add AGENTS.md content
|
|
68
|
+
from code_muse.agents._builder import load_muse_rules
|
|
69
|
+
|
|
70
|
+
agent_rules = load_muse_rules()
|
|
71
|
+
if agent_rules:
|
|
72
|
+
instructions += f"\n\n{agent_rules}"
|
|
73
|
+
|
|
74
|
+
# Apply plugin prompt additions
|
|
75
|
+
from code_muse import callbacks as cb
|
|
76
|
+
|
|
77
|
+
prompt_additions = cb.on_load_prompt()
|
|
78
|
+
if prompt_additions:
|
|
79
|
+
instructions += "\n" + "\n".join(prompt_additions)
|
|
80
|
+
|
|
81
|
+
# Prepare prompt for model (handles claude-code models etc.)
|
|
82
|
+
prepared = prepare_prompt_for_model(
|
|
83
|
+
model_name,
|
|
84
|
+
instructions,
|
|
85
|
+
prompt,
|
|
86
|
+
prepend_system_to_user=True,
|
|
87
|
+
)
|
|
88
|
+
instructions = prepared.instructions
|
|
89
|
+
prompt = prepared.user_prompt
|
|
90
|
+
|
|
91
|
+
model_settings = make_model_settings(model_name)
|
|
92
|
+
|
|
93
|
+
temp_agent = Agent(
|
|
94
|
+
model=model,
|
|
95
|
+
instructions=instructions,
|
|
96
|
+
output_type=str,
|
|
97
|
+
retries=3,
|
|
98
|
+
toolsets=[],
|
|
99
|
+
history_processors=[make_history_processor(agent_config)],
|
|
100
|
+
model_settings=model_settings,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
# Register the tools the agent needs
|
|
104
|
+
agent_tools = agent_config.get_available_tools()
|
|
105
|
+
register_tools_for_agent(temp_agent, agent_tools, model_name=model_name)
|
|
106
|
+
|
|
107
|
+
# Use subagent_stream_handler for clean output
|
|
108
|
+
stream_handler = partial(
|
|
109
|
+
subagent_stream_handler, session_id=f"{agent_name}-plan-session"
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
with subagent_context(agent_name):
|
|
113
|
+
run_ctxs = on_agent_run_context(agent_config, temp_agent, group_id)
|
|
114
|
+
async with AsyncExitStack() as stack:
|
|
115
|
+
for cm in run_ctxs:
|
|
116
|
+
await stack.enter_async_context(cm)
|
|
117
|
+
result = await temp_agent.run(
|
|
118
|
+
prompt,
|
|
119
|
+
message_history=[],
|
|
120
|
+
usage_limits=UsageLimits(request_limit=get_message_limit()),
|
|
121
|
+
event_stream_handler=stream_handler,
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
return _AgentResult(response_text=result.output)
|
|
125
|
+
|
|
126
|
+
except Exception as exc:
|
|
127
|
+
logger.exception("invoke_agent failed for %s", agent_name)
|
|
128
|
+
return _AgentResult(error=str(exc))
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
# ---------------------------------------------------------------------------
|
|
132
|
+
# Slash-command help
|
|
133
|
+
# ---------------------------------------------------------------------------
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def _custom_help() -> list[tuple[str, str]]:
|
|
137
|
+
"""Provide help entry for /help display."""
|
|
138
|
+
return [
|
|
139
|
+
("plan", "Invoke the Planning Agent to create an execution roadmap"),
|
|
140
|
+
]
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
# ---------------------------------------------------------------------------
|
|
144
|
+
# Slash-command handler
|
|
145
|
+
# ---------------------------------------------------------------------------
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
async def _handle_custom_command(command: str, name: str) -> bool | None:
|
|
149
|
+
"""Handle the /plan slash command.
|
|
150
|
+
|
|
151
|
+
Usage:
|
|
152
|
+
/plan <description of what to plan>
|
|
153
|
+
|
|
154
|
+
Invokes the planning-agent with the user's request and displays
|
|
155
|
+
the resulting plan.
|
|
156
|
+
|
|
157
|
+
Returns:
|
|
158
|
+
True if handled.
|
|
159
|
+
None if the command name doesn't match.
|
|
160
|
+
"""
|
|
161
|
+
if name != "plan":
|
|
162
|
+
return None
|
|
163
|
+
|
|
164
|
+
# Extract the planning request (everything after "/plan")
|
|
165
|
+
parts = command.split(maxsplit=1)
|
|
166
|
+
plan_request = parts[1].strip() if len(parts) > 1 else ""
|
|
167
|
+
|
|
168
|
+
if not plan_request:
|
|
169
|
+
emit_warning(
|
|
170
|
+
"Usage: /plan <description of what to plan>\n"
|
|
171
|
+
"Example: /plan Add user authentication with OAuth2"
|
|
172
|
+
)
|
|
173
|
+
return True
|
|
174
|
+
|
|
175
|
+
emit_info("π Planning Agent is creating your roadmapβ¦")
|
|
176
|
+
|
|
177
|
+
result = await _invoke_agent(
|
|
178
|
+
agent_name="planning-agent",
|
|
179
|
+
prompt=(
|
|
180
|
+
f"Create a detailed execution plan for the following task:\n\n"
|
|
181
|
+
f"{plan_request}\n\n"
|
|
182
|
+
f"Include: project structure analysis, dependencies, "
|
|
183
|
+
f"execution steps, and validation strategy."
|
|
184
|
+
),
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
# Display the plan
|
|
188
|
+
if result.response_text:
|
|
189
|
+
emit_success("π Planning Agent β Execution Roadmap")
|
|
190
|
+
emit_info(result.response_text)
|
|
191
|
+
elif result.error:
|
|
192
|
+
emit_warning(f"Planning Agent error: {result.error}")
|
|
193
|
+
else:
|
|
194
|
+
emit_warning("Planning Agent returned no output.")
|
|
195
|
+
|
|
196
|
+
return True # Handled β no additional model invocation needed
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
# ---------------------------------------------------------------------------
|
|
200
|
+
# Register callbacks
|
|
201
|
+
# ---------------------------------------------------------------------------
|
|
202
|
+
|
|
203
|
+
register_callback("custom_command_help", _custom_help)
|
|
204
|
+
register_callback("custom_command", _handle_custom_command)
|
|
205
|
+
|
|
206
|
+
logger.debug("Plan Command plugin callbacks registered")
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""Plan Mode plugin for Muse.
|
|
2
|
+
|
|
3
|
+
Provides tools and hooks to enter/exit a planning-only mode where
|
|
4
|
+
destructive tools (write, replace, delete, shell) are blocked while
|
|
5
|
+
research tools (read, list, grep, ask) remain available.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from code_muse.plugins.plan_mode.mode_cycling import cycle_mode
|
|
9
|
+
from code_muse.plugins.plan_mode.plan_generation import generate_plan_md, save_plan
|
|
10
|
+
from code_muse.plugins.plan_mode.plan_hooks import plan_mode_pre_tool_call_hook
|
|
11
|
+
from code_muse.plugins.plan_mode.plan_mode_tools import (
|
|
12
|
+
PlanModeState,
|
|
13
|
+
get_current_mode,
|
|
14
|
+
get_plan_goal,
|
|
15
|
+
register_approve_plan,
|
|
16
|
+
register_enter_plan_mode,
|
|
17
|
+
register_exit_plan_mode,
|
|
18
|
+
register_get_plan_mode,
|
|
19
|
+
register_open_plan_in_editor,
|
|
20
|
+
set_plan_mode,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"PlanModeState",
|
|
25
|
+
"get_current_mode",
|
|
26
|
+
"get_plan_goal",
|
|
27
|
+
"set_plan_mode",
|
|
28
|
+
"register_enter_plan_mode",
|
|
29
|
+
"register_exit_plan_mode",
|
|
30
|
+
"register_get_plan_mode",
|
|
31
|
+
"register_approve_plan",
|
|
32
|
+
"register_open_plan_in_editor",
|
|
33
|
+
"generate_plan_md",
|
|
34
|
+
"save_plan",
|
|
35
|
+
"cycle_mode",
|
|
36
|
+
"plan_mode_pre_tool_call_hook",
|
|
37
|
+
]
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""Mode cycling logic and UI emission.
|
|
2
|
+
|
|
3
|
+
Provides a ``cycle_mode()`` function that rotates through the three
|
|
4
|
+
plan-mode states. A ``/mode`` slash command exposes this in the UI.
|
|
5
|
+
|
|
6
|
+
TODO: Shift+Tab key-binding integration requires future prompt_toolkit
|
|
7
|
+
hooking (not implemented here to avoid core changes).
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from code_muse.messaging import emit_info
|
|
11
|
+
from code_muse.plugins.plan_mode.plan_mode_tools import (
|
|
12
|
+
PlanModeState,
|
|
13
|
+
get_current_mode,
|
|
14
|
+
set_plan_mode,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
# Cyclic ordering: DEFAULT β AUTO_EDIT β PLAN β DEFAULT
|
|
18
|
+
_MODE_CYCLE = [
|
|
19
|
+
PlanModeState.DEFAULT,
|
|
20
|
+
PlanModeState.AUTO_EDIT,
|
|
21
|
+
PlanModeState.PLAN,
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def cycle_mode() -> PlanModeState:
|
|
26
|
+
"""Advance to the next mode in the cycle and emit a UI indicator.
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
The new active mode.
|
|
30
|
+
"""
|
|
31
|
+
current = get_current_mode()
|
|
32
|
+
try:
|
|
33
|
+
idx = _MODE_CYCLE.index(current)
|
|
34
|
+
except ValueError:
|
|
35
|
+
idx = 0
|
|
36
|
+
next_idx = (idx + 1) % len(_MODE_CYCLE)
|
|
37
|
+
new_mode = _MODE_CYCLE[next_idx]
|
|
38
|
+
set_plan_mode(new_mode)
|
|
39
|
+
emit_info(f"π Mode cycled to: {new_mode.value}")
|
|
40
|
+
return new_mode
|