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,184 @@
|
|
|
1
|
+
"""Content type detector for shell command output.
|
|
2
|
+
|
|
3
|
+
Sniffs stdout to classify output as JSON, diff, log, HTML, code, or unknown.
|
|
4
|
+
Uses fast heuristics — no LLM calls.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import enum
|
|
8
|
+
import json
|
|
9
|
+
import re
|
|
10
|
+
from typing import ClassVar
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ContentType(enum.Enum):
|
|
14
|
+
"""Output content types."""
|
|
15
|
+
|
|
16
|
+
JSON = "json"
|
|
17
|
+
DIFF = "diff"
|
|
18
|
+
LOG = "log"
|
|
19
|
+
HTML = "html"
|
|
20
|
+
CODE = "code"
|
|
21
|
+
SEARCH = "search"
|
|
22
|
+
UNKNOWN = "unknown"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ContentTypeDetector:
|
|
26
|
+
"""Detect content type from shell command stdout."""
|
|
27
|
+
|
|
28
|
+
# Pre-compiled patterns
|
|
29
|
+
DIFF_HEADER: ClassVar[re.Pattern] = re.compile(
|
|
30
|
+
r"^@@\s+-(\d+),?\d*\s+\+(\d+),?\d*\s+@@", re.MULTILINE
|
|
31
|
+
)
|
|
32
|
+
LOG_PATTERNS: ClassVar[list[re.Pattern]] = [
|
|
33
|
+
re.compile(r"^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}"), # ISO timestamp
|
|
34
|
+
re.compile(r"^\d{2}:\d{2}:\d{2}\.\d{3}"), # HH:MM:SS.mmm
|
|
35
|
+
re.compile(r"^\[?\d{4}-\d{2}-\d{2}\]?"), # [YYYY-MM-DD]
|
|
36
|
+
re.compile(r"^\w+\s+\d+\s+\d{2}:\d{2}:\d{2}"), # syslog style
|
|
37
|
+
re.compile(r"^(ERROR|WARN|INFO|DEBUG|TRACE|FATAL)\b", re.IGNORECASE),
|
|
38
|
+
]
|
|
39
|
+
HTML_PATTERNS: ClassVar[list[re.Pattern]] = [
|
|
40
|
+
re.compile(r"<html[\s>]", re.IGNORECASE),
|
|
41
|
+
re.compile(r"<!DOCTYPE\s+html", re.IGNORECASE),
|
|
42
|
+
re.compile(r"<head[\s>]", re.IGNORECASE),
|
|
43
|
+
re.compile(r"<body[\s>]", re.IGNORECASE),
|
|
44
|
+
re.compile(r"<div[\s>]", re.IGNORECASE),
|
|
45
|
+
re.compile(
|
|
46
|
+
r"</?(?:html|head|body|div|span|p|a|table|tr|td)[\s>]", re.IGNORECASE
|
|
47
|
+
),
|
|
48
|
+
]
|
|
49
|
+
CODE_KEYWORDS: ClassVar[set[str]] = {
|
|
50
|
+
"def",
|
|
51
|
+
"class",
|
|
52
|
+
"import",
|
|
53
|
+
"from",
|
|
54
|
+
"function",
|
|
55
|
+
"const",
|
|
56
|
+
"let",
|
|
57
|
+
"var",
|
|
58
|
+
"return",
|
|
59
|
+
"if",
|
|
60
|
+
"else",
|
|
61
|
+
"for",
|
|
62
|
+
"while",
|
|
63
|
+
"try",
|
|
64
|
+
"except",
|
|
65
|
+
"catch",
|
|
66
|
+
"public",
|
|
67
|
+
"private",
|
|
68
|
+
"protected",
|
|
69
|
+
"static",
|
|
70
|
+
"void",
|
|
71
|
+
"int",
|
|
72
|
+
"string",
|
|
73
|
+
"package",
|
|
74
|
+
"export",
|
|
75
|
+
"require",
|
|
76
|
+
"module",
|
|
77
|
+
}
|
|
78
|
+
SEARCH_PATTERNS: ClassVar[list[re.Pattern]] = [
|
|
79
|
+
re.compile(r"^Found\s+\d+\s+results?", re.IGNORECASE),
|
|
80
|
+
re.compile(r"^\d+\s+matches?", re.IGNORECASE),
|
|
81
|
+
re.compile(r"^---\s+search\s+results?\s+---", re.IGNORECASE),
|
|
82
|
+
re.compile(r"^\d+:\d+:.*\x1b", re.MULTILINE), # grep -n output
|
|
83
|
+
]
|
|
84
|
+
|
|
85
|
+
@classmethod
|
|
86
|
+
def detect(cls, stdout: str) -> ContentType:
|
|
87
|
+
"""Detect content type from stdout string.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
stdout: The full stdout from the shell command.
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
ContentType enum value.
|
|
94
|
+
"""
|
|
95
|
+
if not stdout or not stdout.strip():
|
|
96
|
+
return ContentType.UNKNOWN
|
|
97
|
+
|
|
98
|
+
text = stdout.strip()
|
|
99
|
+
|
|
100
|
+
# 1. JSON detection — try to parse
|
|
101
|
+
if cls._is_json(text):
|
|
102
|
+
return ContentType.JSON
|
|
103
|
+
|
|
104
|
+
# 2. Diff detection — unified diff headers
|
|
105
|
+
if cls.DIFF_HEADER.search(text):
|
|
106
|
+
return ContentType.DIFF
|
|
107
|
+
|
|
108
|
+
# 3. Log detection — timestamp patterns or log levels
|
|
109
|
+
if cls._is_log(text):
|
|
110
|
+
return ContentType.LOG
|
|
111
|
+
|
|
112
|
+
# 4. HTML detection
|
|
113
|
+
if cls._is_html(text):
|
|
114
|
+
return ContentType.HTML
|
|
115
|
+
|
|
116
|
+
# 5. Search results detection
|
|
117
|
+
if cls._is_search(text):
|
|
118
|
+
return ContentType.SEARCH
|
|
119
|
+
|
|
120
|
+
# 6. Code detection — keyword density
|
|
121
|
+
if cls._is_code(text):
|
|
122
|
+
return ContentType.CODE
|
|
123
|
+
|
|
124
|
+
return ContentType.UNKNOWN
|
|
125
|
+
|
|
126
|
+
# ------------------------------------------------------------------
|
|
127
|
+
# Private heuristics
|
|
128
|
+
# ------------------------------------------------------------------
|
|
129
|
+
|
|
130
|
+
@classmethod
|
|
131
|
+
def _is_json(cls, text: str) -> bool:
|
|
132
|
+
"""Test if text is valid JSON (object or array)."""
|
|
133
|
+
# Fast check: first non-whitespace char is { or [
|
|
134
|
+
first = text.lstrip()[0] if text else ""
|
|
135
|
+
if first not in ("{", "["):
|
|
136
|
+
return False
|
|
137
|
+
try:
|
|
138
|
+
json.loads(text)
|
|
139
|
+
return True
|
|
140
|
+
except ValueError:
|
|
141
|
+
return False
|
|
142
|
+
|
|
143
|
+
@classmethod
|
|
144
|
+
def _is_log(cls, text: str) -> bool:
|
|
145
|
+
"""Test if text looks like log output."""
|
|
146
|
+
lines = text.splitlines()
|
|
147
|
+
if not lines:
|
|
148
|
+
return False
|
|
149
|
+
# Count lines matching log patterns
|
|
150
|
+
log_lines = 0
|
|
151
|
+
for line in lines[:50]: # sample first 50 lines
|
|
152
|
+
for pat in cls.LOG_PATTERNS:
|
|
153
|
+
if pat.search(line):
|
|
154
|
+
log_lines += 1
|
|
155
|
+
break
|
|
156
|
+
# If > 30% of sampled lines match, treat as log
|
|
157
|
+
return log_lines > max(1, min(len(lines), 50) * 0.3)
|
|
158
|
+
|
|
159
|
+
@classmethod
|
|
160
|
+
def _is_html(cls, text: str) -> bool:
|
|
161
|
+
"""Test if text looks like HTML."""
|
|
162
|
+
return any(pat.search(text[:2000]) for pat in cls.HTML_PATTERNS)
|
|
163
|
+
|
|
164
|
+
@classmethod
|
|
165
|
+
def _is_code(cls, text: str) -> bool:
|
|
166
|
+
"""Test if text looks like source code by keyword density."""
|
|
167
|
+
lines = text.splitlines()
|
|
168
|
+
if not lines:
|
|
169
|
+
return False
|
|
170
|
+
words = text.lower().split()
|
|
171
|
+
if not words:
|
|
172
|
+
return False
|
|
173
|
+
code_words = sum(1 for w in words[:500] if w in cls.CODE_KEYWORDS)
|
|
174
|
+
# If > 5% of words are code keywords, treat as code
|
|
175
|
+
return code_words > max(3, min(len(words), 500) * 0.05)
|
|
176
|
+
|
|
177
|
+
@classmethod
|
|
178
|
+
def _is_search(cls, text: str) -> bool:
|
|
179
|
+
"""Test if text looks like search results (grep/rg/find output)."""
|
|
180
|
+
for pat in cls.SEARCH_PATTERNS:
|
|
181
|
+
if pat.search(text[:2000]):
|
|
182
|
+
return True
|
|
183
|
+
# Also check for filename:line: pattern (grep -n)
|
|
184
|
+
return re.search(r"^\S+\.\w+:\d+:", text[:2000], re.MULTILINE) is not None
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
"""Filter dispatcher for the filter engine.
|
|
2
|
+
|
|
3
|
+
Orchestrates classification → strategy lookup → execution → compression.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import logging
|
|
7
|
+
import re
|
|
8
|
+
from datetime import UTC, datetime
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from code_muse.plugins.filter_engine.classifier import CommandClassifier
|
|
13
|
+
from code_muse.plugins.filter_engine.registry import get_registry
|
|
14
|
+
from code_muse.plugins.filter_engine.verbosity import get_verbosity
|
|
15
|
+
from code_muse.tools.command_runner import ShellCommandOutput, _execute_shell_command
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class FilterDispatcher:
|
|
21
|
+
"""Singleton dispatcher that handles the full filter pipeline."""
|
|
22
|
+
|
|
23
|
+
_instance: FilterDispatcher | None = None
|
|
24
|
+
|
|
25
|
+
def __init__(self) -> None:
|
|
26
|
+
"""Initialise the dispatcher with its classifier."""
|
|
27
|
+
self.classifier = CommandClassifier()
|
|
28
|
+
|
|
29
|
+
@classmethod
|
|
30
|
+
def get_instance(cls) -> FilterDispatcher:
|
|
31
|
+
"""Return the module-level singleton."""
|
|
32
|
+
if cls._instance is None:
|
|
33
|
+
cls._instance = cls()
|
|
34
|
+
return cls._instance
|
|
35
|
+
|
|
36
|
+
@staticmethod
|
|
37
|
+
def _normalize_command(command: str) -> str:
|
|
38
|
+
"""Rewrite plain ``git status`` to force porcelain format.
|
|
39
|
+
|
|
40
|
+
Appends ``--porcelain -b`` when the command is ``git status`` (or
|
|
41
|
+
variants like ``git -C <dir> status``) and does not already request
|
|
42
|
+
porcelain or short output.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
command: The raw shell command.
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
The original command, or a rewritten version with porcelain flags.
|
|
49
|
+
"""
|
|
50
|
+
stripped = command.strip()
|
|
51
|
+
# Must contain git + status as separate words
|
|
52
|
+
if not re.search(r"\bgit\b.*\bstatus\b", stripped):
|
|
53
|
+
return command
|
|
54
|
+
|
|
55
|
+
tokens = stripped.split()
|
|
56
|
+
# Already porcelain/short — leave alone
|
|
57
|
+
if any(t in {"--porcelain", "--short", "-s"} for t in tokens):
|
|
58
|
+
return command
|
|
59
|
+
|
|
60
|
+
return f"{stripped} --porcelain -b"
|
|
61
|
+
|
|
62
|
+
async def handle(
|
|
63
|
+
self,
|
|
64
|
+
context: Any,
|
|
65
|
+
command: str,
|
|
66
|
+
cwd: str | None,
|
|
67
|
+
timeout: int,
|
|
68
|
+
) -> dict[str, Any] | None:
|
|
69
|
+
"""Run the full filter pipeline for a shell command.
|
|
70
|
+
|
|
71
|
+
Steps:
|
|
72
|
+
|
|
73
|
+
1. Classify the command.
|
|
74
|
+
2. Look up the strategy for the category.
|
|
75
|
+
3. If the strategy is ``None`` or the ``unknown`` passthrough,
|
|
76
|
+
return ``None`` so normal execution proceeds.
|
|
77
|
+
4. Execute the command via :func:`_execute_shell_command`.
|
|
78
|
+
5. Apply the strategy to the resulting :class:`ShellCommandOutput`.
|
|
79
|
+
6. Return ``{"pre_executed": True, "output": filtered_output}``.
|
|
80
|
+
|
|
81
|
+
On any strategy exception the error is logged and ``None`` is returned
|
|
82
|
+
so the raw output is preserved as a fallback.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
context: pydantic-ai RunContext (unused).
|
|
86
|
+
command: The shell command to execute.
|
|
87
|
+
cwd: Working directory.
|
|
88
|
+
timeout: Timeout in seconds.
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
A pre-executed result dict or ``None`` for passthrough.
|
|
92
|
+
"""
|
|
93
|
+
category = self.classifier.classify(command)
|
|
94
|
+
registry = get_registry()
|
|
95
|
+
|
|
96
|
+
# Passthrough strategies return None
|
|
97
|
+
if category == "unknown":
|
|
98
|
+
return None
|
|
99
|
+
|
|
100
|
+
verbosity = get_verbosity()
|
|
101
|
+
|
|
102
|
+
# If verbosity is RAW, skip filtering entirely
|
|
103
|
+
if verbosity.value >= 4:
|
|
104
|
+
return None
|
|
105
|
+
|
|
106
|
+
try:
|
|
107
|
+
# Execute the command (sub-agents run silently)
|
|
108
|
+
from code_muse.tools.subagent_context import is_subagent
|
|
109
|
+
|
|
110
|
+
silent = is_subagent()
|
|
111
|
+
group_id = f"filter_engine_{id(command)}"
|
|
112
|
+
|
|
113
|
+
# Force porcelain for plain git status commands so the strategy
|
|
114
|
+
# parser always receives machine-readable output.
|
|
115
|
+
effective_command = self._normalize_command(command)
|
|
116
|
+
if effective_command != command:
|
|
117
|
+
logger.debug(
|
|
118
|
+
"FilterDispatcher: rewritten %r → %r", command, effective_command
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
# _execute_shell_command is async, but we are already in async context
|
|
122
|
+
output = await _execute_shell_command(
|
|
123
|
+
command=effective_command,
|
|
124
|
+
cwd=cwd,
|
|
125
|
+
timeout=timeout,
|
|
126
|
+
group_id=group_id,
|
|
127
|
+
silent=silent,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
# -----------------------------------------------------------------
|
|
131
|
+
# Content-type routing (Epic 019)
|
|
132
|
+
# -----------------------------------------------------------------
|
|
133
|
+
from code_muse.plugins.filter_engine.content_detector import (
|
|
134
|
+
ContentType,
|
|
135
|
+
ContentTypeDetector,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
content_type = ContentTypeDetector.detect(output.stdout or "")
|
|
139
|
+
logger.debug(
|
|
140
|
+
"Detected content type: %s for command: %s", content_type.value, command
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
content_strategy_map = {
|
|
144
|
+
ContentType.JSON: "json",
|
|
145
|
+
ContentType.DIFF: "diff",
|
|
146
|
+
ContentType.LOG: "log",
|
|
147
|
+
ContentType.HTML: "html",
|
|
148
|
+
ContentType.SEARCH: "search",
|
|
149
|
+
ContentType.CODE: category,
|
|
150
|
+
ContentType.UNKNOWN: category,
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
effective_category = content_strategy_map.get(content_type, category)
|
|
154
|
+
|
|
155
|
+
strategy = registry.get_strategy(effective_category)
|
|
156
|
+
if strategy is None and effective_category != category:
|
|
157
|
+
logger.debug(
|
|
158
|
+
"No strategy for %s, falling back to command category %s",
|
|
159
|
+
effective_category,
|
|
160
|
+
category,
|
|
161
|
+
)
|
|
162
|
+
effective_category = category
|
|
163
|
+
strategy = registry.get_strategy(effective_category)
|
|
164
|
+
|
|
165
|
+
if strategy is None:
|
|
166
|
+
return None
|
|
167
|
+
|
|
168
|
+
# Apply strategy
|
|
169
|
+
filtered = strategy(
|
|
170
|
+
command,
|
|
171
|
+
output.stdout or "",
|
|
172
|
+
output.stderr or "",
|
|
173
|
+
output.exit_code or 0,
|
|
174
|
+
verbosity,
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
if filtered is None:
|
|
178
|
+
return None
|
|
179
|
+
|
|
180
|
+
# Track execution metrics (best-effort, never blocks)
|
|
181
|
+
try:
|
|
182
|
+
from code_muse.plugins.token_tracking.record import record_command
|
|
183
|
+
|
|
184
|
+
record_command(
|
|
185
|
+
command=command,
|
|
186
|
+
raw_stdout=output.stdout or "",
|
|
187
|
+
raw_stderr=output.stderr or "",
|
|
188
|
+
compressed_stdout=filtered.stdout or "",
|
|
189
|
+
compressed_stderr=filtered.stderr or "",
|
|
190
|
+
category=effective_category,
|
|
191
|
+
strategy=strategy.__name__
|
|
192
|
+
if hasattr(strategy, "__name__")
|
|
193
|
+
else str(strategy),
|
|
194
|
+
exit_code=output.exit_code or 0,
|
|
195
|
+
)
|
|
196
|
+
except Exception:
|
|
197
|
+
pass
|
|
198
|
+
|
|
199
|
+
return {"pre_executed": True, "output": filtered}
|
|
200
|
+
except Exception:
|
|
201
|
+
logger.exception("FilterDispatcher: strategy failed for %r", command)
|
|
202
|
+
# Tee recovery: save raw output so user can recover
|
|
203
|
+
try:
|
|
204
|
+
import tempfile
|
|
205
|
+
|
|
206
|
+
raw_stdout = (
|
|
207
|
+
getattr(locals().get("output"), "stdout", "(unavailable)")
|
|
208
|
+
or "(empty)"
|
|
209
|
+
)
|
|
210
|
+
raw_stderr = (
|
|
211
|
+
getattr(locals().get("output"), "stderr", "(unavailable)")
|
|
212
|
+
or "(empty)"
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
tee_dir = Path(tempfile.gettempdir()) / "muse_tee"
|
|
216
|
+
tee_dir.mkdir(exist_ok=True)
|
|
217
|
+
tee_path = (
|
|
218
|
+
tee_dir
|
|
219
|
+
/ f"tee_{datetime.now(UTC).strftime('%Y%m%d_%H%M%S')}_{hash(command) & 0xFFFF:04x}.txt"
|
|
220
|
+
)
|
|
221
|
+
tee_path.write_text(
|
|
222
|
+
f"# Raw output saved after filter error\n"
|
|
223
|
+
f"# Command: {command}\n"
|
|
224
|
+
f"# Timestamp: {datetime.now(UTC).isoformat()}\n\n"
|
|
225
|
+
f"STDOUT:\n{raw_stdout}\n\n"
|
|
226
|
+
f"STDERR:\n{raw_stderr}\n"
|
|
227
|
+
)
|
|
228
|
+
tee_path.chmod(0o600) # user-only readable
|
|
229
|
+
logger.info("Tee recovery: raw output saved to %s", tee_path)
|
|
230
|
+
# Return hint that includes the tee path
|
|
231
|
+
return {
|
|
232
|
+
"pre_executed": True,
|
|
233
|
+
"output": ShellCommandOutput(
|
|
234
|
+
success=False,
|
|
235
|
+
command=command,
|
|
236
|
+
stdout=f"⚠ Filter error — raw output saved to {tee_path}",
|
|
237
|
+
stderr="",
|
|
238
|
+
exit_code=-1,
|
|
239
|
+
execution_time=0.0,
|
|
240
|
+
),
|
|
241
|
+
}
|
|
242
|
+
except Exception as tee_exc:
|
|
243
|
+
logger.error("Tee recovery itself failed: %s", tee_exc)
|
|
244
|
+
return None
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"""Filter Engine plugin for Muse — callback registration.
|
|
2
|
+
|
|
3
|
+
Registers:
|
|
4
|
+
- ``run_shell_command`` callback that intercepts shell commands
|
|
5
|
+
- ``/init`` custom command for one-command project setup
|
|
6
|
+
- Startup hook for tee-file cleanup
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import logging
|
|
10
|
+
import tempfile
|
|
11
|
+
import time
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
from code_muse.callbacks import register_callback
|
|
16
|
+
from code_muse.messaging import emit_info, emit_success, emit_warning
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# ---------------------------------------------------------------------------
|
|
22
|
+
# Cython JIT hooks — safe fallback to pure Python
|
|
23
|
+
# ---------------------------------------------------------------------------
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _setup_cython_hooks() -> None:
|
|
27
|
+
"""Enable pyximport so .pyx modules compile on-the-fly.
|
|
28
|
+
|
|
29
|
+
Falls back gracefully when Cython is missing or compilation fails.
|
|
30
|
+
"""
|
|
31
|
+
try:
|
|
32
|
+
import pyximport
|
|
33
|
+
|
|
34
|
+
pyximport.install(
|
|
35
|
+
language_level=3,
|
|
36
|
+
build_in_temp=True,
|
|
37
|
+
inplace=True,
|
|
38
|
+
)
|
|
39
|
+
import code_muse
|
|
40
|
+
|
|
41
|
+
if code_muse.CYTHON_ENABLED:
|
|
42
|
+
emit_success(
|
|
43
|
+
f"✅ Cython enabled — {code_muse.PYX_MODULE_COUNT} modules compiled"
|
|
44
|
+
)
|
|
45
|
+
else:
|
|
46
|
+
emit_warning("⚠️ Cython not available — running in pure Python mode")
|
|
47
|
+
except ImportError:
|
|
48
|
+
emit_warning("⚠️ Cython not available — running in pure Python mode")
|
|
49
|
+
except Exception: # noqa: BLE001
|
|
50
|
+
emit_warning("⚠️ Cython not available — running in pure Python mode")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
# ---------------------------------------------------------------------------
|
|
54
|
+
# Startup hook — tee file cleanup
|
|
55
|
+
# ---------------------------------------------------------------------------
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _on_startup() -> None:
|
|
59
|
+
"""Delete tee files older than 24 hours on startup."""
|
|
60
|
+
try:
|
|
61
|
+
tee_dir = Path(tempfile.gettempdir()) / "muse_tee"
|
|
62
|
+
if tee_dir.exists():
|
|
63
|
+
now = time.time()
|
|
64
|
+
for f in tee_dir.iterdir():
|
|
65
|
+
if f.is_file() and now - f.stat().st_mtime > 86400:
|
|
66
|
+
f.unlink(missing_ok=True)
|
|
67
|
+
except Exception:
|
|
68
|
+
pass
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
# ---------------------------------------------------------------------------
|
|
72
|
+
# Run-shell-command callback
|
|
73
|
+
# ---------------------------------------------------------------------------
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
async def filter_engine_callback(
|
|
77
|
+
context: Any,
|
|
78
|
+
command: str,
|
|
79
|
+
cwd: str | None = None,
|
|
80
|
+
timeout: int = 60,
|
|
81
|
+
) -> dict[str, Any] | None:
|
|
82
|
+
"""Run-shell-command callback for the filter engine.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
context: The pydantic-ai RunContext (unused).
|
|
86
|
+
command: The shell command to potentially intercept.
|
|
87
|
+
cwd: Working directory for the command.
|
|
88
|
+
timeout: Timeout in seconds.
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
``{"pre_executed": True, "output": ShellCommandOutput(...)}`` when the
|
|
92
|
+
filter engine handles the command, or ``None`` to let normal execution
|
|
93
|
+
proceed.
|
|
94
|
+
"""
|
|
95
|
+
from code_muse.plugins.filter_engine.dispatcher import FilterDispatcher
|
|
96
|
+
|
|
97
|
+
dispatcher = FilterDispatcher.get_instance()
|
|
98
|
+
return await dispatcher.handle(context, command, cwd, timeout)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
# ---------------------------------------------------------------------------
|
|
102
|
+
# /init custom command
|
|
103
|
+
# ---------------------------------------------------------------------------
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
_INIT_MARKDOWN = """# Fast-Puppy Token Saving
|
|
107
|
+
|
|
108
|
+
This project uses Fast-Puppy to compress shell command output, reducing token usage by 60-90%.
|
|
109
|
+
|
|
110
|
+
## Enabled Strategies
|
|
111
|
+
- Git output compression (status, log, diff)
|
|
112
|
+
- Test runner failure focus
|
|
113
|
+
- Lint output grouping
|
|
114
|
+
- Code-aware read filtering
|
|
115
|
+
|
|
116
|
+
## Configuration
|
|
117
|
+
See `~/.muse/config.toml` for global settings.
|
|
118
|
+
|
|
119
|
+
## Commands
|
|
120
|
+
Run `/tracking gain` to see token savings.
|
|
121
|
+
"""
|
|
122
|
+
|
|
123
|
+
_PROJECT_FILES = [
|
|
124
|
+
"pyproject.toml",
|
|
125
|
+
"package.json",
|
|
126
|
+
"Cargo.toml",
|
|
127
|
+
"go.mod",
|
|
128
|
+
"Gemfile",
|
|
129
|
+
]
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _detect_project_type(cwd: Path) -> str | None:
|
|
133
|
+
"""Detect project type from known manifest files."""
|
|
134
|
+
for manifest in _PROJECT_FILES:
|
|
135
|
+
if (cwd / manifest).exists():
|
|
136
|
+
return manifest
|
|
137
|
+
return None
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def _on_custom_command(command: str, name: str) -> bool | None: # noqa: ARG001
|
|
141
|
+
"""Handle ``/init`` — one-command Fast-Puppy setup."""
|
|
142
|
+
if name != "init":
|
|
143
|
+
return None
|
|
144
|
+
|
|
145
|
+
cwd = Path.cwd()
|
|
146
|
+
manifest = _detect_project_type(cwd)
|
|
147
|
+
|
|
148
|
+
md_path = cwd / "FAST_PUPPY.md"
|
|
149
|
+
if md_path.exists():
|
|
150
|
+
emit_info("FAST_PUPPY.md already exists — nothing to do.")
|
|
151
|
+
return True
|
|
152
|
+
|
|
153
|
+
md_path.write_text(_INIT_MARKDOWN, encoding="utf-8")
|
|
154
|
+
if manifest:
|
|
155
|
+
emit_success(
|
|
156
|
+
f"✅ Fast-Puppy initialized. Detected {manifest}. "
|
|
157
|
+
f"Created FAST_PUPPY.md. Filter engine is active."
|
|
158
|
+
)
|
|
159
|
+
else:
|
|
160
|
+
emit_success(
|
|
161
|
+
"✅ Fast-Puppy initialized. Created FAST_PUPPY.md. Filter engine is active."
|
|
162
|
+
)
|
|
163
|
+
return True
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
# ---------------------------------------------------------------------------
|
|
167
|
+
# Help entries
|
|
168
|
+
# ---------------------------------------------------------------------------
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def _on_custom_command_help() -> list[tuple[str, str]]:
|
|
172
|
+
return [
|
|
173
|
+
("/init", "Initialize Fast-Puppy in the current project"),
|
|
174
|
+
]
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
# ---------------------------------------------------------------------------
|
|
178
|
+
# Register all callbacks
|
|
179
|
+
# ---------------------------------------------------------------------------
|
|
180
|
+
|
|
181
|
+
register_callback("startup", _on_startup)
|
|
182
|
+
register_callback("startup", _setup_cython_hooks)
|
|
183
|
+
# Priority: runs first (before policy_engine=50). Performs content-type detection and routing.
|
|
184
|
+
register_callback("run_shell_command", filter_engine_callback, priority=10)
|
|
185
|
+
register_callback("custom_command", _on_custom_command)
|
|
186
|
+
register_callback("custom_command_help", _on_custom_command_help)
|
|
187
|
+
|
|
188
|
+
logger.debug("Filter Engine plugin callbacks registered")
|