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,441 @@
|
|
|
1
|
+
"""File Permission Handler Plugin.
|
|
2
|
+
|
|
3
|
+
This plugin handles user permission prompts for file operations,
|
|
4
|
+
providing a consistent and extensible permission system.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import contextlib
|
|
8
|
+
import difflib
|
|
9
|
+
import threading
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
from rich.text import Text as RichText
|
|
14
|
+
|
|
15
|
+
from code_muse.callbacks import register_callback
|
|
16
|
+
from code_muse.config import get_diff_context_lines, get_yolo_mode
|
|
17
|
+
from code_muse.messaging import emit_warning
|
|
18
|
+
from code_muse.tools.common import (
|
|
19
|
+
_find_best_window,
|
|
20
|
+
get_user_approval,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
# Lock for preventing multiple simultaneous permission prompts.
|
|
24
|
+
# FREE-THREADED: Sync-only file confirmation; keep threading.Lock.
|
|
25
|
+
_FILE_CONFIRMATION_LOCK = threading.Lock()
|
|
26
|
+
|
|
27
|
+
# Thread-local storage for user feedback from permission prompts
|
|
28
|
+
_thread_local = threading.local()
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def get_last_user_feedback() -> str | None:
|
|
32
|
+
"""Get the last user feedback from a permission prompt in this thread.
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
The user feedback string, or None if no feedback was provided.
|
|
36
|
+
"""
|
|
37
|
+
return getattr(_thread_local, "last_user_feedback", None)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _set_user_feedback(feedback: str | None) -> None:
|
|
41
|
+
"""Store user feedback in thread-local storage."""
|
|
42
|
+
_thread_local.last_user_feedback = feedback
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def clear_user_feedback() -> None:
|
|
46
|
+
"""Clear any stored user feedback."""
|
|
47
|
+
_thread_local.last_user_feedback = None
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def set_diff_already_shown(shown: bool = True) -> None:
|
|
51
|
+
"""Mark that a diff preview was already shown during permission prompt."""
|
|
52
|
+
_thread_local.diff_already_shown = shown
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def was_diff_already_shown() -> bool:
|
|
56
|
+
"""Check if a diff was already shown during the permission prompt.
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
True if diff was shown, False otherwise
|
|
60
|
+
"""
|
|
61
|
+
return getattr(_thread_local, "diff_already_shown", False)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def clear_diff_shown_flag() -> None:
|
|
65
|
+
"""Clear the diff-already-shown flag."""
|
|
66
|
+
_thread_local.diff_already_shown = False
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
# Diff formatting is now handled by common.format_diff_with_colors()
|
|
70
|
+
# Arrow selector and approval UI now handled by common.get_user_approval()
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _preview_delete_snippet(file_path: str, snippet: str) -> str | None:
|
|
74
|
+
"""Generate a preview diff for deleting a snippet without modifying the file."""
|
|
75
|
+
try:
|
|
76
|
+
file_path = Path(file_path).resolve()
|
|
77
|
+
if not file_path.exists() or not file_path.is_file():
|
|
78
|
+
return None
|
|
79
|
+
|
|
80
|
+
with open(file_path, encoding="utf-8", errors="surrogateescape") as f:
|
|
81
|
+
original = f.read()
|
|
82
|
+
|
|
83
|
+
# Sanitize any surrogate characters
|
|
84
|
+
with contextlib.suppress(UnicodeEncodeError, UnicodeDecodeError):
|
|
85
|
+
original = original.encode("utf-8", errors="surrogatepass").decode(
|
|
86
|
+
"utf-8", errors="replace"
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
if snippet not in original:
|
|
90
|
+
return None
|
|
91
|
+
|
|
92
|
+
modified = original.replace(snippet, "")
|
|
93
|
+
diff_text = "".join(
|
|
94
|
+
difflib.unified_diff(
|
|
95
|
+
original.splitlines(keepends=True),
|
|
96
|
+
modified.splitlines(keepends=True),
|
|
97
|
+
fromfile=f"a/{file_path.name}",
|
|
98
|
+
tofile=f"b/{file_path.name}",
|
|
99
|
+
n=get_diff_context_lines(),
|
|
100
|
+
)
|
|
101
|
+
)
|
|
102
|
+
return diff_text
|
|
103
|
+
except Exception:
|
|
104
|
+
return None
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _preview_write_to_file(
|
|
108
|
+
file_path: str, content: str, overwrite: bool = False
|
|
109
|
+
) -> str | None:
|
|
110
|
+
"""Generate a preview diff for writing to a file without modifying it."""
|
|
111
|
+
try:
|
|
112
|
+
file_path = Path(file_path).resolve()
|
|
113
|
+
exists = file_path.exists()
|
|
114
|
+
|
|
115
|
+
if exists and not overwrite:
|
|
116
|
+
return None
|
|
117
|
+
|
|
118
|
+
diff_lines = difflib.unified_diff(
|
|
119
|
+
[] if not exists else [""],
|
|
120
|
+
content.splitlines(keepends=True),
|
|
121
|
+
fromfile="/dev/null" if not exists else f"a/{file_path.name}",
|
|
122
|
+
tofile=f"b/{file_path.name}",
|
|
123
|
+
n=get_diff_context_lines(),
|
|
124
|
+
)
|
|
125
|
+
return "".join(diff_lines)
|
|
126
|
+
except Exception:
|
|
127
|
+
return None
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _preview_replace_in_file(
|
|
131
|
+
file_path: str, replacements: list[dict[str, str]]
|
|
132
|
+
) -> str | None:
|
|
133
|
+
"""Generate a preview diff for replacing text in a file without modifying the file."""
|
|
134
|
+
try:
|
|
135
|
+
file_path = Path(file_path).resolve()
|
|
136
|
+
|
|
137
|
+
with open(file_path, encoding="utf-8", errors="surrogateescape") as f:
|
|
138
|
+
original = f.read()
|
|
139
|
+
|
|
140
|
+
# Sanitize any surrogate characters
|
|
141
|
+
with contextlib.suppress(UnicodeEncodeError, UnicodeDecodeError):
|
|
142
|
+
original = original.encode("utf-8", errors="surrogatepass").decode(
|
|
143
|
+
"utf-8", errors="replace"
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
modified = original
|
|
147
|
+
for rep in replacements:
|
|
148
|
+
old_snippet = rep.get("old_str", "")
|
|
149
|
+
new_snippet = rep.get("new_str", "")
|
|
150
|
+
|
|
151
|
+
if old_snippet and old_snippet in modified:
|
|
152
|
+
modified = modified.replace(old_snippet, new_snippet)
|
|
153
|
+
continue
|
|
154
|
+
|
|
155
|
+
# Use the same logic as file_modifications for fuzzy matching
|
|
156
|
+
orig_lines = modified.splitlines()
|
|
157
|
+
loc, score = _find_best_window(orig_lines, old_snippet)
|
|
158
|
+
|
|
159
|
+
if score < 0.95 or loc is None:
|
|
160
|
+
return None
|
|
161
|
+
|
|
162
|
+
start, end = loc
|
|
163
|
+
had_trailing_newline = modified.endswith("\n")
|
|
164
|
+
prefix = "\n".join(orig_lines[:start])
|
|
165
|
+
suffix = "\n".join(orig_lines[end:])
|
|
166
|
+
parts = []
|
|
167
|
+
if prefix:
|
|
168
|
+
parts.append(prefix)
|
|
169
|
+
parts.append(new_snippet.rstrip("\n"))
|
|
170
|
+
if suffix:
|
|
171
|
+
parts.append(suffix)
|
|
172
|
+
modified = "\n".join(parts)
|
|
173
|
+
if had_trailing_newline and not modified.endswith("\n"):
|
|
174
|
+
modified += "\n"
|
|
175
|
+
|
|
176
|
+
if modified == original:
|
|
177
|
+
return None
|
|
178
|
+
|
|
179
|
+
diff_text = "".join(
|
|
180
|
+
difflib.unified_diff(
|
|
181
|
+
original.splitlines(keepends=True),
|
|
182
|
+
modified.splitlines(keepends=True),
|
|
183
|
+
fromfile=f"a/{file_path.name}",
|
|
184
|
+
tofile=f"b/{file_path.name}",
|
|
185
|
+
n=get_diff_context_lines(),
|
|
186
|
+
)
|
|
187
|
+
)
|
|
188
|
+
return diff_text
|
|
189
|
+
except Exception:
|
|
190
|
+
return None
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def _preview_delete_file(file_path: str) -> str | None:
|
|
194
|
+
"""Whole-file deletes intentionally do not show content diffs."""
|
|
195
|
+
return None
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def prompt_for_file_permission(
|
|
199
|
+
file_path: str,
|
|
200
|
+
operation: str,
|
|
201
|
+
preview: str | None = None,
|
|
202
|
+
message_group: str | None = None,
|
|
203
|
+
) -> tuple[bool, str | None]:
|
|
204
|
+
"""Prompt the user for permission to perform a file operation.
|
|
205
|
+
|
|
206
|
+
This function provides a unified permission prompt system for all file operations.
|
|
207
|
+
|
|
208
|
+
Args:
|
|
209
|
+
file_path: Path to the file being modified.
|
|
210
|
+
operation: Description of the operation (e.g., "edit", "delete", "create").
|
|
211
|
+
preview: Optional preview of changes (diff or content preview).
|
|
212
|
+
message_group: Optional message group for organizing output.
|
|
213
|
+
|
|
214
|
+
Returns:
|
|
215
|
+
Tuple of (confirmed: bool, user_feedback: str | None)
|
|
216
|
+
- confirmed: True if permission is granted, False otherwise
|
|
217
|
+
- user_feedback: Optional feedback message from user to send back to the model
|
|
218
|
+
"""
|
|
219
|
+
yolo_mode = get_yolo_mode()
|
|
220
|
+
|
|
221
|
+
# Skip confirmation only if in yolo mode (removed TTY check for better compatibility)
|
|
222
|
+
if yolo_mode:
|
|
223
|
+
return True, None
|
|
224
|
+
|
|
225
|
+
# Try to acquire the lock to prevent multiple simultaneous prompts
|
|
226
|
+
confirmation_lock_acquired = _FILE_CONFIRMATION_LOCK.acquire(blocking=False)
|
|
227
|
+
if not confirmation_lock_acquired:
|
|
228
|
+
emit_warning(
|
|
229
|
+
"Another file operation is currently awaiting confirmation",
|
|
230
|
+
message_group=message_group,
|
|
231
|
+
)
|
|
232
|
+
return False, None
|
|
233
|
+
|
|
234
|
+
try:
|
|
235
|
+
# Build panel content
|
|
236
|
+
panel_content = RichText()
|
|
237
|
+
panel_content.append("🔒 Requesting permission to ", style="bold yellow")
|
|
238
|
+
panel_content.append(operation, style="bold cyan")
|
|
239
|
+
panel_content.append(":\n", style="bold yellow")
|
|
240
|
+
panel_content.append("📄 ", style="dim")
|
|
241
|
+
panel_content.append(file_path, style="bold white")
|
|
242
|
+
|
|
243
|
+
# Use the common approval function
|
|
244
|
+
confirmed, user_feedback = get_user_approval(
|
|
245
|
+
title="File Operation",
|
|
246
|
+
content=panel_content,
|
|
247
|
+
preview=preview,
|
|
248
|
+
border_style="dim white",
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
return confirmed, user_feedback
|
|
252
|
+
|
|
253
|
+
finally:
|
|
254
|
+
if confirmation_lock_acquired:
|
|
255
|
+
_FILE_CONFIRMATION_LOCK.release()
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def handle_edit_file_permission(
|
|
259
|
+
context: Any,
|
|
260
|
+
file_path: str,
|
|
261
|
+
operation_type: str,
|
|
262
|
+
operation_data: Any,
|
|
263
|
+
message_group: str | None = None,
|
|
264
|
+
) -> bool:
|
|
265
|
+
"""Handle permission for edit_file operations with automatic preview generation.
|
|
266
|
+
|
|
267
|
+
Args:
|
|
268
|
+
context: The operation context
|
|
269
|
+
file_path: Path to the file being operated on
|
|
270
|
+
operation_type: Type of edit operation ('write', 'replace', 'delete_snippet')
|
|
271
|
+
operation_data: Operation-specific data (content, replacements, snippet, etc.)
|
|
272
|
+
message_group: Optional message group
|
|
273
|
+
|
|
274
|
+
Returns:
|
|
275
|
+
True if permission granted, False if denied
|
|
276
|
+
"""
|
|
277
|
+
preview = None
|
|
278
|
+
|
|
279
|
+
if operation_type == "write":
|
|
280
|
+
content = operation_data.get("content", "")
|
|
281
|
+
overwrite = operation_data.get("overwrite", False)
|
|
282
|
+
preview = _preview_write_to_file(file_path, content, overwrite)
|
|
283
|
+
operation_desc = "write to"
|
|
284
|
+
elif operation_type == "replace":
|
|
285
|
+
replacements = operation_data.get("replacements", [])
|
|
286
|
+
preview = _preview_replace_in_file(file_path, replacements)
|
|
287
|
+
operation_desc = "replace text in"
|
|
288
|
+
elif operation_type == "delete_snippet":
|
|
289
|
+
snippet = operation_data.get("delete_snippet", "")
|
|
290
|
+
preview = _preview_delete_snippet(file_path, snippet)
|
|
291
|
+
operation_desc = "delete snippet from"
|
|
292
|
+
else:
|
|
293
|
+
operation_desc = f"perform {operation_type} operation on"
|
|
294
|
+
|
|
295
|
+
confirmed, user_feedback = prompt_for_file_permission(
|
|
296
|
+
file_path, operation_desc, preview, message_group
|
|
297
|
+
)
|
|
298
|
+
# Store feedback in thread-local storage so the tool can access it
|
|
299
|
+
_set_user_feedback(user_feedback)
|
|
300
|
+
return confirmed
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def handle_delete_file_permission(
|
|
304
|
+
context: Any,
|
|
305
|
+
file_path: str,
|
|
306
|
+
message_group: str | None = None,
|
|
307
|
+
) -> bool:
|
|
308
|
+
"""Handle permission for delete_file operations with automatic preview generation.
|
|
309
|
+
|
|
310
|
+
Args:
|
|
311
|
+
context: The operation context
|
|
312
|
+
file_path: Path to the file being deleted
|
|
313
|
+
message_group: Optional message group
|
|
314
|
+
|
|
315
|
+
Returns:
|
|
316
|
+
True if permission granted, False if denied
|
|
317
|
+
"""
|
|
318
|
+
preview = _preview_delete_file(file_path)
|
|
319
|
+
confirmed, user_feedback = prompt_for_file_permission(
|
|
320
|
+
file_path, "delete", preview, message_group
|
|
321
|
+
)
|
|
322
|
+
# Store feedback in thread-local storage so the tool can access it
|
|
323
|
+
_set_user_feedback(user_feedback)
|
|
324
|
+
return confirmed
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def handle_file_permission(
|
|
328
|
+
context: Any,
|
|
329
|
+
file_path: str,
|
|
330
|
+
operation: str,
|
|
331
|
+
preview: str | None = None,
|
|
332
|
+
message_group: str | None = None,
|
|
333
|
+
operation_data: Any = None,
|
|
334
|
+
) -> bool:
|
|
335
|
+
"""Callback handler for file permission checks.
|
|
336
|
+
|
|
337
|
+
This function is called by file operations to check for user permission.
|
|
338
|
+
It returns True if the operation should proceed, False if it should be cancelled.
|
|
339
|
+
|
|
340
|
+
Args:
|
|
341
|
+
context: The operation context
|
|
342
|
+
file_path: Path to the file being operated on
|
|
343
|
+
operation: Description of the operation
|
|
344
|
+
preview: Optional preview of changes (deprecated - use operation_data instead)
|
|
345
|
+
message_group: Optional message group
|
|
346
|
+
operation_data: Operation-specific data for preview generation
|
|
347
|
+
|
|
348
|
+
Returns:
|
|
349
|
+
True if permission granted, False if denied
|
|
350
|
+
"""
|
|
351
|
+
# Generate preview from operation_data if provided
|
|
352
|
+
if operation_data is not None:
|
|
353
|
+
preview = _generate_preview_from_operation_data(
|
|
354
|
+
file_path, operation, operation_data
|
|
355
|
+
)
|
|
356
|
+
|
|
357
|
+
confirmed, user_feedback = prompt_for_file_permission(
|
|
358
|
+
file_path, operation, preview, message_group
|
|
359
|
+
)
|
|
360
|
+
# Store feedback in thread-local storage so the tool can access it
|
|
361
|
+
_set_user_feedback(user_feedback)
|
|
362
|
+
return confirmed
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
def _generate_preview_from_operation_data(
|
|
366
|
+
file_path: str, operation: str, operation_data: Any
|
|
367
|
+
) -> str | None:
|
|
368
|
+
"""Generate preview diff from operation data.
|
|
369
|
+
|
|
370
|
+
Args:
|
|
371
|
+
file_path: Path to the file
|
|
372
|
+
operation: Type of operation
|
|
373
|
+
operation_data: Operation-specific data
|
|
374
|
+
|
|
375
|
+
Returns:
|
|
376
|
+
Preview diff or None if generation fails
|
|
377
|
+
"""
|
|
378
|
+
try:
|
|
379
|
+
if operation == "delete":
|
|
380
|
+
return _preview_delete_file(file_path)
|
|
381
|
+
elif operation == "write":
|
|
382
|
+
content = operation_data.get("content", "")
|
|
383
|
+
overwrite = operation_data.get("overwrite", False)
|
|
384
|
+
return _preview_write_to_file(file_path, content, overwrite)
|
|
385
|
+
elif operation == "delete snippet from":
|
|
386
|
+
snippet = operation_data.get("snippet", "")
|
|
387
|
+
return _preview_delete_snippet(file_path, snippet)
|
|
388
|
+
elif operation == "replace text in":
|
|
389
|
+
replacements = operation_data.get("replacements", [])
|
|
390
|
+
return _preview_replace_in_file(file_path, replacements)
|
|
391
|
+
elif operation == "edit_file":
|
|
392
|
+
# Handle edit_file operations
|
|
393
|
+
if "delete_snippet" in operation_data:
|
|
394
|
+
return _preview_delete_snippet(
|
|
395
|
+
file_path, operation_data["delete_snippet"]
|
|
396
|
+
)
|
|
397
|
+
elif "replacements" in operation_data:
|
|
398
|
+
return _preview_replace_in_file(
|
|
399
|
+
file_path, operation_data["replacements"]
|
|
400
|
+
)
|
|
401
|
+
elif "content" in operation_data:
|
|
402
|
+
content = operation_data.get("content", "")
|
|
403
|
+
overwrite = operation_data.get("overwrite", False)
|
|
404
|
+
return _preview_write_to_file(file_path, content, overwrite)
|
|
405
|
+
|
|
406
|
+
return None
|
|
407
|
+
except Exception:
|
|
408
|
+
return None
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
def get_permission_handler_help() -> str:
|
|
412
|
+
"""Return help information for the file permission handler."""
|
|
413
|
+
return """File Permission Handler Plugin:
|
|
414
|
+
- Unified permission prompts for all file operations
|
|
415
|
+
- YOLO mode support for automatic approval
|
|
416
|
+
- Thread-safe confirmation system
|
|
417
|
+
- Consistent user experience across file operations
|
|
418
|
+
- Detailed preview support with diff highlighting
|
|
419
|
+
- Automatic preview generation from operation data"""
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
def get_file_permission_prompt_additions() -> str:
|
|
423
|
+
"""Return file permission handling prompt additions for agents."""
|
|
424
|
+
if get_yolo_mode():
|
|
425
|
+
return ""
|
|
426
|
+
|
|
427
|
+
return """
|
|
428
|
+
## User Approval System
|
|
429
|
+
|
|
430
|
+
When file operations are rejected, the response includes a `user_feedback` field:
|
|
431
|
+
- If `user_feedback` has text: implement their suggestion and retry the operation.
|
|
432
|
+
- If `user_feedback` is empty: stop and ask the user what they want instead.
|
|
433
|
+
- Never retry the exact same rejected operation without changes.
|
|
434
|
+
"""
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
# Register the callback for file permission handling
|
|
438
|
+
register_callback("file_permission", handle_file_permission)
|
|
439
|
+
|
|
440
|
+
# Register the prompt hook for file permission instructions
|
|
441
|
+
register_callback("load_prompt", get_file_permission_prompt_additions)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""Filter Engine plugin for Muse.
|
|
2
|
+
|
|
3
|
+
Intercepts shell commands, classifies them, applies compression strategies,
|
|
4
|
+
and returns compact results to reduce token usage.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from code_muse.plugins.filter_engine.classifier import CommandClassifier
|
|
8
|
+
from code_muse.plugins.filter_engine.dispatcher import FilterDispatcher
|
|
9
|
+
from code_muse.plugins.filter_engine.register_callbacks import (
|
|
10
|
+
filter_engine_callback,
|
|
11
|
+
)
|
|
12
|
+
from code_muse.plugins.filter_engine.registry import StrategyRegistry
|
|
13
|
+
|
|
14
|
+
# Import strategies so they self-register with the strategy registry
|
|
15
|
+
from code_muse.plugins.filter_engine.strategies import ( # noqa: F401
|
|
16
|
+
code,
|
|
17
|
+
git,
|
|
18
|
+
lint,
|
|
19
|
+
test,
|
|
20
|
+
)
|
|
21
|
+
from code_muse.plugins.filter_engine.verbosity import VerbosityLevel, get_verbosity
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"CommandClassifier",
|
|
25
|
+
"FilterDispatcher",
|
|
26
|
+
"StrategyRegistry",
|
|
27
|
+
"VerbosityLevel",
|
|
28
|
+
"filter_engine_callback",
|
|
29
|
+
"get_verbosity",
|
|
30
|
+
]
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"""Command classifier for the filter engine.
|
|
2
|
+
|
|
3
|
+
Uses pre-compiled regex patterns to classify shell commands into categories
|
|
4
|
+
that determine which compression strategy is applied.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import re
|
|
8
|
+
from typing import ClassVar
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class CommandClassifier:
|
|
12
|
+
"""Classify shell commands into categories using regex patterns."""
|
|
13
|
+
|
|
14
|
+
# Pre-compiled pattern sets for each category
|
|
15
|
+
PATTERNS: ClassVar[dict[str, list[re.Pattern[str]]]] = {
|
|
16
|
+
"git": [
|
|
17
|
+
re.compile(r"^\s*git\s+status"),
|
|
18
|
+
re.compile(r"^\s*git\s+log"),
|
|
19
|
+
re.compile(r"^\s*git\s+diff"),
|
|
20
|
+
re.compile(r"^\s*git\s+add"),
|
|
21
|
+
re.compile(r"^\s*git\s+commit"),
|
|
22
|
+
re.compile(r"^\s*git\s+push"),
|
|
23
|
+
re.compile(r"^\s*git\s+pull"),
|
|
24
|
+
re.compile(r"^\s*git\s+fetch"),
|
|
25
|
+
re.compile(r"^\s*git\s+branch"),
|
|
26
|
+
re.compile(r"^\s*git\s+checkout"),
|
|
27
|
+
re.compile(r"^\s*git\s+merge"),
|
|
28
|
+
re.compile(r"^\s*git\s+rebase"),
|
|
29
|
+
re.compile(r"^\s*git\s+show"),
|
|
30
|
+
re.compile(r"^\s*git\s+blame"),
|
|
31
|
+
re.compile(r"^\s*git\s+stash"),
|
|
32
|
+
re.compile(r"^\s*git\s+reset"),
|
|
33
|
+
re.compile(r"^\s*git\s+tag"),
|
|
34
|
+
re.compile(r"^\s*git\s+clone"),
|
|
35
|
+
re.compile(r"^\s*git\s+init"),
|
|
36
|
+
re.compile(r"^\s*git\s+remote"),
|
|
37
|
+
re.compile(r"^\s*git\s+config"),
|
|
38
|
+
re.compile(r"^\s*git\s+\S"), # catch-all for any other git command
|
|
39
|
+
],
|
|
40
|
+
"test": [
|
|
41
|
+
re.compile(r"^\s*pytest\b"),
|
|
42
|
+
re.compile(r"^\s*python\s+-m\s+pytest\b"),
|
|
43
|
+
re.compile(r"^\s*vitest\b"),
|
|
44
|
+
re.compile(r"^\s*jest\b"),
|
|
45
|
+
re.compile(r"^\s*cargo\s+test\b"),
|
|
46
|
+
re.compile(r"^\s*rspec\b"),
|
|
47
|
+
re.compile(r"^\s*go\s+test\b"),
|
|
48
|
+
re.compile(r"^\s*npm\s+test\b"),
|
|
49
|
+
re.compile(r"^\s*yarn\s+test\b"),
|
|
50
|
+
re.compile(r"^\s*npx\s+jest\b"),
|
|
51
|
+
re.compile(r"^\s*npx\s+vitest\b"),
|
|
52
|
+
re.compile(r"^\s*python\s+-m\s+unittest\b"),
|
|
53
|
+
re.compile(r"^\s*mvn\s+test\b"),
|
|
54
|
+
re.compile(r"^\s*gradle\s+test\b"),
|
|
55
|
+
re.compile(r"^\s*tox\b"),
|
|
56
|
+
re.compile(r"^\s*nox\b"),
|
|
57
|
+
],
|
|
58
|
+
"lint": [
|
|
59
|
+
re.compile(r"^\s*ruff\b"),
|
|
60
|
+
re.compile(r"^\s*eslint\b"),
|
|
61
|
+
re.compile(r"^\s*tsc\b"),
|
|
62
|
+
re.compile(r"^\s*golangci-lint\b"),
|
|
63
|
+
re.compile(r"^\s*rubocop\b"),
|
|
64
|
+
re.compile(r"^\s*flake8\b"),
|
|
65
|
+
re.compile(r"^\s*mypy\b"),
|
|
66
|
+
re.compile(r"^\s*pyright\b"),
|
|
67
|
+
re.compile(r"^\s*cargo\s+clippy\b"),
|
|
68
|
+
re.compile(r"^\s*clippy\b"),
|
|
69
|
+
re.compile(r"^\s*shellcheck\b"),
|
|
70
|
+
re.compile(r"^\s*markdownlint\b"),
|
|
71
|
+
re.compile(r"^\s*pylint\b"),
|
|
72
|
+
re.compile(r"^\s*black\s+--check\b"),
|
|
73
|
+
re.compile(r"^\s*isort\s+--check\b"),
|
|
74
|
+
re.compile(r"^\s*prettier\s+--check\b"),
|
|
75
|
+
],
|
|
76
|
+
"code": [
|
|
77
|
+
re.compile(r"^\s*cat\b"),
|
|
78
|
+
re.compile(r"^\s*head\b"),
|
|
79
|
+
re.compile(r"^\s*tail\b"),
|
|
80
|
+
re.compile(r"^\s*less\b"),
|
|
81
|
+
re.compile(r"^\s*bat\b"),
|
|
82
|
+
re.compile(r"^\s*nl\b"),
|
|
83
|
+
re.compile(r"^\s*sed\b"),
|
|
84
|
+
re.compile(r"^\s*awk\b"),
|
|
85
|
+
re.compile(r"^\s*grep\b"),
|
|
86
|
+
re.compile(r"^\s*rg\b"),
|
|
87
|
+
re.compile(r"^\s*find\b"),
|
|
88
|
+
re.compile(r"^\s*ls\b"),
|
|
89
|
+
re.compile(r"^\s*tree\b"),
|
|
90
|
+
re.compile(r"^\s*wc\b"),
|
|
91
|
+
re.compile(r"^\s*sort\b"),
|
|
92
|
+
re.compile(r"^\s*uniq\b"),
|
|
93
|
+
re.compile(r"^\s*xargs\b"),
|
|
94
|
+
re.compile(r"^\s*cut\b"),
|
|
95
|
+
re.compile(r"^\s*tr\b"),
|
|
96
|
+
re.compile(r"^\s*dd\b"),
|
|
97
|
+
re.compile(r"^\s*diff\b"),
|
|
98
|
+
re.compile(r"^\s*cmp\b"),
|
|
99
|
+
],
|
|
100
|
+
"read": [
|
|
101
|
+
re.compile(r"^\s*cat\b"),
|
|
102
|
+
re.compile(r"^\s*head\b"),
|
|
103
|
+
re.compile(r"^\s*tail\b"),
|
|
104
|
+
re.compile(r"^\s*less\b"),
|
|
105
|
+
re.compile(r"^\s*bat\b"),
|
|
106
|
+
],
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
@classmethod
|
|
110
|
+
def classify(cls, command: str) -> str:
|
|
111
|
+
"""Classify a shell command into a category.
|
|
112
|
+
|
|
113
|
+
Categories are checked in priority order: git, test, lint, code, read.
|
|
114
|
+
The ``read`` category is a subset of ``code``; if a command matches both,
|
|
115
|
+
it is classified as ``read`` because read is more specific.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
command: The raw shell command string.
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
One of ``git``, ``test``, ``lint``, ``code``, ``read``, or ``unknown``.
|
|
122
|
+
"""
|
|
123
|
+
if not command or not command.strip():
|
|
124
|
+
return "unknown"
|
|
125
|
+
|
|
126
|
+
stripped = command.strip()
|
|
127
|
+
|
|
128
|
+
# Check git first (most specific)
|
|
129
|
+
for pattern in cls.PATTERNS["git"]:
|
|
130
|
+
if pattern.search(stripped):
|
|
131
|
+
return "git"
|
|
132
|
+
|
|
133
|
+
# Check test
|
|
134
|
+
for pattern in cls.PATTERNS["test"]:
|
|
135
|
+
if pattern.search(stripped):
|
|
136
|
+
return "test"
|
|
137
|
+
|
|
138
|
+
# Check lint
|
|
139
|
+
for pattern in cls.PATTERNS["lint"]:
|
|
140
|
+
if pattern.search(stripped):
|
|
141
|
+
return "lint"
|
|
142
|
+
|
|
143
|
+
# Check read (subset of code, checked before code)
|
|
144
|
+
for pattern in cls.PATTERNS["read"]:
|
|
145
|
+
if pattern.search(stripped):
|
|
146
|
+
return "read"
|
|
147
|
+
|
|
148
|
+
# Check code
|
|
149
|
+
for pattern in cls.PATTERNS["code"]:
|
|
150
|
+
if pattern.search(stripped):
|
|
151
|
+
return "code"
|
|
152
|
+
|
|
153
|
+
return "unknown"
|