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,276 @@
|
|
|
1
|
+
"""Shell Minimizer plugin — callback registration.
|
|
2
|
+
|
|
3
|
+
Registers:
|
|
4
|
+
- ``run_shell_command`` callback that intercepts shell commands,
|
|
5
|
+
matches them against built-in (and user) pipeline definitions,
|
|
6
|
+
executes them, applies the minimizer pipeline, and returns the
|
|
7
|
+
compressed result to the LLM.
|
|
8
|
+
- ``/minimizer`` custom command to show stats, toggle verbosity,
|
|
9
|
+
and list active filters.
|
|
10
|
+
- ``startup`` hook for logging and built-in filter loading.
|
|
11
|
+
- ``custom_command_help`` entries for slash-command discovery.
|
|
12
|
+
|
|
13
|
+
NOTE: We use the ``run_shell_command`` hook (not ``post_tool_call``)
|
|
14
|
+
because ``post_tool_call`` fires *after* the tool result is already
|
|
15
|
+
returned to the LLM, whereas ``run_shell_command`` lets us intercept
|
|
16
|
+
and modify the result *before* it reaches the model.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import logging
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
from typing import Any
|
|
22
|
+
|
|
23
|
+
from code_muse.callbacks import register_callback
|
|
24
|
+
from code_muse.messaging import emit_info
|
|
25
|
+
from code_muse.plugins.filter_engine.verbosity import get_verbosity
|
|
26
|
+
from code_muse.tools.command_runner import ShellCommandOutput, _execute_shell_command
|
|
27
|
+
from code_muse.tools.subagent_context import is_subagent
|
|
28
|
+
|
|
29
|
+
logger = logging.getLogger(__name__)
|
|
30
|
+
|
|
31
|
+
# ---------------------------------------------------------------------------
|
|
32
|
+
# State
|
|
33
|
+
# ---------------------------------------------------------------------------
|
|
34
|
+
|
|
35
|
+
_pipelines: list = [] # list[CompiledPipeline], loaded lazily
|
|
36
|
+
_stats: dict[str, int] = {"intercepted": 0, "bytes_saved": 0, "pipelines": 0}
|
|
37
|
+
_disabled: bool = False
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _is_disabled() -> bool:
|
|
41
|
+
"""Check whether the minimizer has been toggled off via ``/minimizer off``."""
|
|
42
|
+
return _disabled
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
# ---------------------------------------------------------------------------
|
|
46
|
+
# Pipeline loading
|
|
47
|
+
# ---------------------------------------------------------------------------
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _load_builtin_pipelines() -> list:
|
|
51
|
+
"""Load and compile pipelines from the built-in TOML file."""
|
|
52
|
+
from code_muse.plugins.shell_minimizer.pipeline import parse_pipeline_toml
|
|
53
|
+
|
|
54
|
+
builtin_path = Path(__file__).parent / "builtin_filters.toml"
|
|
55
|
+
if not builtin_path.exists():
|
|
56
|
+
logger.warning("builtin_filters.toml not found at %s", builtin_path)
|
|
57
|
+
return []
|
|
58
|
+
|
|
59
|
+
try:
|
|
60
|
+
raw = builtin_path.read_text(encoding="utf-8")
|
|
61
|
+
return parse_pipeline_toml(raw, str(builtin_path))
|
|
62
|
+
except Exception as exc:
|
|
63
|
+
logger.error("Failed to load builtin filters: %s", exc)
|
|
64
|
+
return []
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _get_pipelines() -> list:
|
|
68
|
+
"""Return the compiled pipeline list, loading on first access."""
|
|
69
|
+
global _pipelines
|
|
70
|
+
if not _pipelines:
|
|
71
|
+
_pipelines = _load_builtin_pipelines()
|
|
72
|
+
_stats["pipelines"] = len(_pipelines)
|
|
73
|
+
logger.debug("Loaded %d minimizer pipelines", len(_pipelines))
|
|
74
|
+
return _pipelines
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
# ---------------------------------------------------------------------------
|
|
78
|
+
# Command matching
|
|
79
|
+
# ---------------------------------------------------------------------------
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _find_pipeline(command: str) -> Any | None:
|
|
83
|
+
"""Return the first pipeline whose match patterns accept *command*.
|
|
84
|
+
|
|
85
|
+
Returns ``None`` when no pipeline matches.
|
|
86
|
+
"""
|
|
87
|
+
from code_muse.plugins.shell_minimizer.pipeline import CompiledPipeline
|
|
88
|
+
|
|
89
|
+
for pipeline in _get_pipelines():
|
|
90
|
+
if isinstance(pipeline, CompiledPipeline) and pipeline.matches_program(command):
|
|
91
|
+
return pipeline
|
|
92
|
+
return None
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
# ---------------------------------------------------------------------------
|
|
96
|
+
# run_shell_command callback
|
|
97
|
+
# ---------------------------------------------------------------------------
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
async def _minimizer_callback(
|
|
101
|
+
context: Any,
|
|
102
|
+
command: str,
|
|
103
|
+
cwd: str | None = None,
|
|
104
|
+
timeout: int = 60,
|
|
105
|
+
) -> dict[str, Any] | None:
|
|
106
|
+
"""Intercept shell commands, apply minimizer pipeline, return compressed output.
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
context: pydantic-ai RunContext (unused).
|
|
110
|
+
command: The shell command string.
|
|
111
|
+
cwd: Working directory.
|
|
112
|
+
timeout: Timeout in seconds.
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
``{"pre_executed": True, "output": ShellCommandOutput(...)}`` when a
|
|
116
|
+
pipeline handles the command, or ``None`` to let normal execution
|
|
117
|
+
or downstream callbacks (filter_engine, build_filter, etc.) proceed.
|
|
118
|
+
"""
|
|
119
|
+
if _is_disabled():
|
|
120
|
+
return None
|
|
121
|
+
|
|
122
|
+
pipeline = _find_pipeline(command)
|
|
123
|
+
if pipeline is None:
|
|
124
|
+
return None # No matching pipeline; let others handle
|
|
125
|
+
|
|
126
|
+
verbosity = get_verbosity()
|
|
127
|
+
if verbosity.value >= 4: # RAW: no filtering
|
|
128
|
+
return None
|
|
129
|
+
|
|
130
|
+
try:
|
|
131
|
+
# Execute the command (sub-agents run silently)
|
|
132
|
+
silent = is_subagent()
|
|
133
|
+
group_id = f"shell_minim_{id(command)}"
|
|
134
|
+
|
|
135
|
+
output = await _execute_shell_command(
|
|
136
|
+
command=command,
|
|
137
|
+
cwd=cwd,
|
|
138
|
+
timeout=timeout,
|
|
139
|
+
group_id=group_id,
|
|
140
|
+
silent=silent,
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
# Combine stdout + stderr for pipeline processing
|
|
144
|
+
raw_text = (output.stdout or "") + (
|
|
145
|
+
"\n" + output.stderr if output.stderr else ""
|
|
146
|
+
)
|
|
147
|
+
exit_code = output.exit_code or 0
|
|
148
|
+
|
|
149
|
+
# Apply pipeline
|
|
150
|
+
compressed = pipeline.apply(raw_text, exit_code)
|
|
151
|
+
|
|
152
|
+
# Update stats
|
|
153
|
+
_stats["intercepted"] += 1
|
|
154
|
+
raw_len = len(raw_text.encode("utf-8"))
|
|
155
|
+
comp_len = len(compressed.encode("utf-8"))
|
|
156
|
+
_stats["bytes_saved"] += max(0, raw_len - comp_len)
|
|
157
|
+
|
|
158
|
+
# Build filtered ShellCommandOutput
|
|
159
|
+
# Preserve original exit_code and success flag
|
|
160
|
+
filtered = ShellCommandOutput(
|
|
161
|
+
success=output.success,
|
|
162
|
+
command=command,
|
|
163
|
+
stdout=compressed,
|
|
164
|
+
stderr="", # stderr is folded into stdout after compression
|
|
165
|
+
exit_code=exit_code,
|
|
166
|
+
execution_time=output.execution_time,
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
# Track token savings (best-effort, never blocks)
|
|
170
|
+
try:
|
|
171
|
+
from code_muse.plugins.token_tracking.record import record_command
|
|
172
|
+
|
|
173
|
+
pipeline_name = getattr(pipeline, "name", "unknown")
|
|
174
|
+
record_command(
|
|
175
|
+
command=command,
|
|
176
|
+
raw_stdout=output.stdout or "",
|
|
177
|
+
raw_stderr=output.stderr or "",
|
|
178
|
+
compressed_stdout=compressed,
|
|
179
|
+
compressed_stderr="",
|
|
180
|
+
category=f"minim_{pipeline_name}",
|
|
181
|
+
strategy="shell_minimizer",
|
|
182
|
+
exit_code=exit_code,
|
|
183
|
+
)
|
|
184
|
+
except Exception:
|
|
185
|
+
pass
|
|
186
|
+
|
|
187
|
+
return {"pre_executed": True, "output": filtered}
|
|
188
|
+
|
|
189
|
+
except Exception:
|
|
190
|
+
logger.exception("Shell Minimizer: pipeline failed for %r", command)
|
|
191
|
+
return None # Fallback to raw execution
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
# ---------------------------------------------------------------------------
|
|
195
|
+
# /minimizer custom command
|
|
196
|
+
# ---------------------------------------------------------------------------
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def _minimizer_help() -> list[tuple[str, str]]:
|
|
200
|
+
return [
|
|
201
|
+
("/minimizer", "Show minimizer status and stats"),
|
|
202
|
+
("/minimizer off", "Disable output minimisation"),
|
|
203
|
+
("/minimizer on", "Re-enable output minimisation"),
|
|
204
|
+
("/minimizer list", "List all active pipeline filters"),
|
|
205
|
+
]
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def _handle_minimizer_command(command: str, name: str) -> bool | str | None:
|
|
209
|
+
"""Handle ``/minimizer`` and its subcommands."""
|
|
210
|
+
global _disabled
|
|
211
|
+
|
|
212
|
+
if name != "minimizer":
|
|
213
|
+
return None
|
|
214
|
+
|
|
215
|
+
tokens = command.strip().split()
|
|
216
|
+
sub = tokens[1] if len(tokens) > 1 else "status"
|
|
217
|
+
|
|
218
|
+
if sub == "off":
|
|
219
|
+
_disabled = True
|
|
220
|
+
emit_info("🔇 Shell Minimizer disabled. Output will pass through unfiltered.")
|
|
221
|
+
return True
|
|
222
|
+
|
|
223
|
+
if sub == "on":
|
|
224
|
+
_disabled = False
|
|
225
|
+
emit_info("🔊 Shell Minimizer re-enabled.")
|
|
226
|
+
return True
|
|
227
|
+
|
|
228
|
+
if sub == "list":
|
|
229
|
+
lines = ["**Active Minimizer Pipelines:**"]
|
|
230
|
+
for p in _get_pipelines():
|
|
231
|
+
name = getattr(p, "name", "?")
|
|
232
|
+
cmd = getattr(p, "match_command", None)
|
|
233
|
+
subc = getattr(p, "match_subcommand", None)
|
|
234
|
+
cmd_pat = cmd.pattern if cmd else "*"
|
|
235
|
+
sub_pat = subc.pattern if subc else "*"
|
|
236
|
+
lines.append(f" • **{name}** → `{cmd_pat}` `{sub_pat}`")
|
|
237
|
+
return "\n".join(lines)
|
|
238
|
+
|
|
239
|
+
# Default: status
|
|
240
|
+
status_lines = [
|
|
241
|
+
f"🔧 **Shell Minimizer** — active ({_stats['pipelines']} pipelines loaded)",
|
|
242
|
+
f" Commands intercepted: {_stats['intercepted']}",
|
|
243
|
+
f" Bytes saved: {_stats['bytes_saved']:,}",
|
|
244
|
+
f" Status: {'🟡 DISABLED' if _disabled else '🟢 enabled'}",
|
|
245
|
+
"",
|
|
246
|
+
"Use `/minimizer list` to see all pipelines.",
|
|
247
|
+
"Use `/minimizer off` / `/minimizer on` to toggle.",
|
|
248
|
+
]
|
|
249
|
+
return "\n".join(status_lines)
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
# ---------------------------------------------------------------------------
|
|
253
|
+
# Startup hook
|
|
254
|
+
# ---------------------------------------------------------------------------
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def _on_startup() -> None:
|
|
258
|
+
"""Log plugin status and pre-load built-in pipelines."""
|
|
259
|
+
try:
|
|
260
|
+
n = len(_get_pipelines())
|
|
261
|
+
logger.info("Shell Minimizer loaded with %d built-in pipelines", n)
|
|
262
|
+
except Exception as exc:
|
|
263
|
+
logger.warning("Shell Minimizer startup check failed: %s", exc)
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
# ---------------------------------------------------------------------------
|
|
267
|
+
# Register all callbacks
|
|
268
|
+
# ---------------------------------------------------------------------------
|
|
269
|
+
|
|
270
|
+
# Priority: runs third (after policy_engine=50). Compresses shell output when no other handler matches.
|
|
271
|
+
register_callback("run_shell_command", _minimizer_callback, priority=-10)
|
|
272
|
+
register_callback("custom_command", _handle_minimizer_command)
|
|
273
|
+
register_callback("custom_command_help", _minimizer_help)
|
|
274
|
+
register_callback("startup", _on_startup)
|
|
275
|
+
|
|
276
|
+
logger.debug("Shell Minimizer plugin callbacks registered")
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""Shell command safety assessment agent.
|
|
2
|
+
|
|
3
|
+
This agent provides rapid risk assessment of shell commands before execution.
|
|
4
|
+
It's designed to be ultra-lightweight with a concise prompt (<200 tokens) and
|
|
5
|
+
uses structured output for reliable parsing.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import TYPE_CHECKING
|
|
9
|
+
|
|
10
|
+
from code_muse.agents.base_agent import BaseAgent
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ShellSafetyAgent(BaseAgent):
|
|
17
|
+
"""Lightweight agent for assessing shell command safety risks.
|
|
18
|
+
|
|
19
|
+
This agent evaluates shell commands for potential risks including:
|
|
20
|
+
- File system destruction (rm -rf, dd, format, mkfs)
|
|
21
|
+
- Database operations (DROP, TRUNCATE, unfiltered UPDATE/DELETE)
|
|
22
|
+
- Privilege escalation (sudo, su, chmod 777)
|
|
23
|
+
- Network operations (wget/curl to unknown hosts)
|
|
24
|
+
- Data exfiltration patterns
|
|
25
|
+
|
|
26
|
+
The agent returns structured output with a risk level and brief reasoning.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def name(self) -> str:
|
|
31
|
+
"""Agent name for internal use."""
|
|
32
|
+
return "shell_safety_checker"
|
|
33
|
+
|
|
34
|
+
@property
|
|
35
|
+
def display_name(self) -> str:
|
|
36
|
+
"""User-facing display name."""
|
|
37
|
+
return "Shell Safety Checker 🛡️"
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
def description(self) -> str:
|
|
41
|
+
"""Agent description."""
|
|
42
|
+
return "Lightweight agent that assesses shell command safety risks"
|
|
43
|
+
|
|
44
|
+
def get_system_prompt(self) -> str:
|
|
45
|
+
"""Get the ultra-concise system prompt for shell safety assessment.
|
|
46
|
+
|
|
47
|
+
This prompt is kept under 200 tokens for fast inference and low cost.
|
|
48
|
+
"""
|
|
49
|
+
return """You are a shell command safety analyzer. Assess risk levels concisely.
|
|
50
|
+
|
|
51
|
+
**Risk Levels:**
|
|
52
|
+
- none: Completely safe (ls, pwd, echo, cat readonly files)
|
|
53
|
+
- low: Minimal risk (mkdir, touch, git status, read-only queries)
|
|
54
|
+
- medium: Moderate risk (file edits, package installs, service restarts)
|
|
55
|
+
- high: Significant risk (rm files, UPDATE/DELETE without WHERE, TRUNCATE, chmod dangerous permissions)
|
|
56
|
+
- critical: Severe/destructive (rm -rf, DROP TABLE/DATABASE, dd, format, mkfs, bq delete dataset, unfiltered mass deletes)
|
|
57
|
+
|
|
58
|
+
**Evaluate:**
|
|
59
|
+
- Scope (single file vs. entire system)
|
|
60
|
+
- Reversibility (can it be undone?)
|
|
61
|
+
- Data loss potential
|
|
62
|
+
- Privilege requirements
|
|
63
|
+
- Database destruction patterns
|
|
64
|
+
|
|
65
|
+
**Output:** Risk level + reasoning (max 1 sentence)."""
|
|
66
|
+
|
|
67
|
+
def get_available_tools(self) -> list[str]:
|
|
68
|
+
"""This agent uses no tools - pure reasoning only."""
|
|
69
|
+
return []
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"""Caching layer for shell command safety assessments.
|
|
2
|
+
|
|
3
|
+
This module provides an LRU cache for recently assessed commands to avoid redundant API calls.
|
|
4
|
+
|
|
5
|
+
The approach is simple and secure: let the LLM assess ALL commands and cache
|
|
6
|
+
those assessments. This eliminates the security risks of pre-defined whitelists
|
|
7
|
+
while providing the performance benefits of caching.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from collections import OrderedDict
|
|
11
|
+
from dataclasses import dataclass
|
|
12
|
+
|
|
13
|
+
# Maximum number of cached assessments (LRU eviction after this)
|
|
14
|
+
MAX_CACHE_SIZE = 200
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass
|
|
18
|
+
class CachedAssessment:
|
|
19
|
+
"""A cached safety assessment result."""
|
|
20
|
+
|
|
21
|
+
risk: str
|
|
22
|
+
reasoning: str
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class CommandSafetyCache:
|
|
26
|
+
"""LRU cache for shell command safety assessments.
|
|
27
|
+
|
|
28
|
+
This cache stores previous LLM assessments to avoid redundant API calls.
|
|
29
|
+
It uses an OrderedDict for O(1) LRU eviction.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
def __init__(self, max_size: int = MAX_CACHE_SIZE):
|
|
33
|
+
self._cache: OrderedDict[tuple[str, str | None], CachedAssessment] = (
|
|
34
|
+
OrderedDict()
|
|
35
|
+
)
|
|
36
|
+
self._max_size = max_size
|
|
37
|
+
self._hits = 0
|
|
38
|
+
self._misses = 0
|
|
39
|
+
|
|
40
|
+
def _make_key(self, command: str, cwd: str | None) -> tuple[str, str | None]:
|
|
41
|
+
"""Create a cache key from command and cwd."""
|
|
42
|
+
# Normalize command (strip whitespace)
|
|
43
|
+
return (command.strip(), cwd)
|
|
44
|
+
|
|
45
|
+
def get(self, command: str, cwd: str | None = None) -> CachedAssessment | None:
|
|
46
|
+
"""Get a cached assessment if it exists.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
command: The shell command
|
|
50
|
+
cwd: Optional working directory
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
CachedAssessment if found, None otherwise
|
|
54
|
+
"""
|
|
55
|
+
key = self._make_key(command, cwd)
|
|
56
|
+
if key in self._cache:
|
|
57
|
+
# Move to end (most recently used)
|
|
58
|
+
self._cache.move_to_end(key)
|
|
59
|
+
self._hits += 1
|
|
60
|
+
return self._cache[key]
|
|
61
|
+
self._misses += 1
|
|
62
|
+
return None
|
|
63
|
+
|
|
64
|
+
def put(self, command: str, cwd: str | None, assessment: CachedAssessment) -> None:
|
|
65
|
+
"""Store an assessment in the cache.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
command: The shell command
|
|
69
|
+
cwd: Optional working directory
|
|
70
|
+
assessment: The assessment result to cache
|
|
71
|
+
"""
|
|
72
|
+
key = self._make_key(command, cwd)
|
|
73
|
+
|
|
74
|
+
# If already exists, update and move to end
|
|
75
|
+
if key in self._cache:
|
|
76
|
+
self._cache.move_to_end(key)
|
|
77
|
+
self._cache[key] = assessment
|
|
78
|
+
return
|
|
79
|
+
|
|
80
|
+
# Evict oldest if at capacity
|
|
81
|
+
while len(self._cache) >= self._max_size:
|
|
82
|
+
self._cache.popitem(last=False)
|
|
83
|
+
|
|
84
|
+
self._cache[key] = assessment
|
|
85
|
+
|
|
86
|
+
def clear(self) -> None:
|
|
87
|
+
"""Clear all cached assessments."""
|
|
88
|
+
self._cache.clear()
|
|
89
|
+
self._hits = 0
|
|
90
|
+
self._misses = 0
|
|
91
|
+
|
|
92
|
+
@property
|
|
93
|
+
def stats(self) -> dict:
|
|
94
|
+
"""Get cache statistics."""
|
|
95
|
+
total = self._hits + self._misses
|
|
96
|
+
hit_rate = (self._hits / total * 100) if total > 0 else 0
|
|
97
|
+
return {
|
|
98
|
+
"size": len(self._cache),
|
|
99
|
+
"max_size": self._max_size,
|
|
100
|
+
"hits": self._hits,
|
|
101
|
+
"misses": self._misses,
|
|
102
|
+
"hit_rate": f"{hit_rate:.1f}%",
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
# Global cache instance (singleton for the session)
|
|
107
|
+
_cache = CommandSafetyCache()
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def get_cache_stats() -> dict:
|
|
111
|
+
"""Get statistics about the cache performance."""
|
|
112
|
+
return _cache.stats
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def get_cached_assessment(
|
|
116
|
+
command: str, cwd: str | None = None
|
|
117
|
+
) -> CachedAssessment | None:
|
|
118
|
+
"""Get a cached command safety assessment.
|
|
119
|
+
|
|
120
|
+
Cache-only approach: use the LLM cache for speed, but let the LLM
|
|
121
|
+
determine safety for all commands. No pre-defined whitelists.
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
command: The shell command to check
|
|
125
|
+
cwd: Optional working directory
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
CachedAssessment if found in cache, None if needs LLM assessment
|
|
129
|
+
"""
|
|
130
|
+
return _cache.get(command, cwd)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def cache_assessment(command: str, cwd: str | None, risk: str, reasoning: str) -> None:
|
|
134
|
+
"""Cache an LLM assessment result.
|
|
135
|
+
|
|
136
|
+
Cache all LLM assessments since the same command should get
|
|
137
|
+
the same assessment, providing both security and performance.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
command: The shell command
|
|
141
|
+
cwd: Optional working directory
|
|
142
|
+
risk: The assessed risk level
|
|
143
|
+
reasoning: The assessment reasoning
|
|
144
|
+
"""
|
|
145
|
+
assessment = CachedAssessment(
|
|
146
|
+
risk=risk,
|
|
147
|
+
reasoning=reasoning,
|
|
148
|
+
)
|
|
149
|
+
_cache.put(command, cwd, assessment)
|