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,626 @@
|
|
|
1
|
+
"""Lint compression strategies for the filter engine.
|
|
2
|
+
|
|
3
|
+
Groups linter output by rule code / message type to produce compact summaries.
|
|
4
|
+
Also handles ``grep`` and ``find`` output for the lint category.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
import re
|
|
9
|
+
from collections import defaultdict
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from code_muse.plugins.filter_engine.registry import get_registry
|
|
13
|
+
from code_muse.plugins.filter_engine.verbosity import VerbosityLevel
|
|
14
|
+
from code_muse.tools.command_runner import ShellCommandOutput
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
_RULE_PATTERN = re.compile(r"\b([A-Z]+\d+)\b")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def compress_ruff(
|
|
21
|
+
stdout: str,
|
|
22
|
+
stderr: str,
|
|
23
|
+
verbosity: VerbosityLevel,
|
|
24
|
+
) -> ShellCommandOutput:
|
|
25
|
+
"""Compress ruff text output by grouping rule codes.
|
|
26
|
+
|
|
27
|
+
Format::
|
|
28
|
+
|
|
29
|
+
E501: 5 files, 12 occurrences
|
|
30
|
+
F841: 3 files, 4 occurrences
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
stdout: Raw ruff stdout.
|
|
34
|
+
stderr: Raw ruff stderr.
|
|
35
|
+
verbosity: Current verbosity level.
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
Compressed :class:`ShellCommandOutput`.
|
|
39
|
+
"""
|
|
40
|
+
# Try JSON first
|
|
41
|
+
try:
|
|
42
|
+
data = json.loads(stdout)
|
|
43
|
+
if isinstance(data, list):
|
|
44
|
+
return _compress_ruff_json(data, stderr, verbosity)
|
|
45
|
+
except ValueError:
|
|
46
|
+
pass
|
|
47
|
+
|
|
48
|
+
lines = stdout.splitlines()
|
|
49
|
+
rule_counts = defaultdict(lambda: {"files": set(), "count": 0})
|
|
50
|
+
|
|
51
|
+
# Pattern: file.py:5:1: E501 Line too long
|
|
52
|
+
pattern = re.compile(r"^(.+?):\d+:\d+:\s*([A-Z]\d+)\s+(.*)")
|
|
53
|
+
|
|
54
|
+
cdef str line
|
|
55
|
+
cdef object match
|
|
56
|
+
cdef str filepath
|
|
57
|
+
cdef str rule
|
|
58
|
+
|
|
59
|
+
for line in lines:
|
|
60
|
+
match = pattern.match(line.rstrip("\r"))
|
|
61
|
+
if match:
|
|
62
|
+
filepath = match.group(1)
|
|
63
|
+
rule = match.group(2)
|
|
64
|
+
rule_counts[rule]["files"].add(filepath)
|
|
65
|
+
rule_counts[rule]["count"] += 1
|
|
66
|
+
|
|
67
|
+
if not rule_counts:
|
|
68
|
+
return ShellCommandOutput(
|
|
69
|
+
success=True,
|
|
70
|
+
command="ruff",
|
|
71
|
+
stdout=stdout.strip() or "No issues found",
|
|
72
|
+
stderr=stderr if verbosity >= VerbosityLevel.VERY_VERBOSE else None,
|
|
73
|
+
exit_code=0,
|
|
74
|
+
execution_time=None,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
parts: list[str] = []
|
|
78
|
+
for rule in sorted(rule_counts.keys()):
|
|
79
|
+
info = rule_counts[rule]
|
|
80
|
+
parts.append(f"{rule}: {len(info['files'])} files, {info['count']} occurrences")
|
|
81
|
+
if verbosity >= VerbosityLevel.VERBOSE:
|
|
82
|
+
for fp in sorted(info["files"])[:10]:
|
|
83
|
+
parts.append(f" {fp}")
|
|
84
|
+
if len(info["files"]) > 10:
|
|
85
|
+
parts.append(f" ... and {len(info['files']) - 10} more files")
|
|
86
|
+
|
|
87
|
+
compressed = "\n".join(parts)
|
|
88
|
+
|
|
89
|
+
return ShellCommandOutput(
|
|
90
|
+
success=True,
|
|
91
|
+
command="ruff",
|
|
92
|
+
stdout=compressed,
|
|
93
|
+
stderr=stderr if verbosity >= VerbosityLevel.VERY_VERBOSE else None,
|
|
94
|
+
exit_code=0,
|
|
95
|
+
execution_time=None,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _compress_ruff_json(
|
|
100
|
+
data: list[dict[str, Any]],
|
|
101
|
+
stderr: str,
|
|
102
|
+
verbosity: VerbosityLevel,
|
|
103
|
+
) -> ShellCommandOutput:
|
|
104
|
+
"""Compress ruff JSON output.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
data: List of violation dicts from ruff ``--format=json``.
|
|
108
|
+
stderr: Raw stderr.
|
|
109
|
+
verbosity: Current verbosity level.
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
Compressed :class:`ShellCommandOutput`.
|
|
113
|
+
"""
|
|
114
|
+
rule_counts = defaultdict(lambda: {"files": set(), "count": 0})
|
|
115
|
+
|
|
116
|
+
cdef str rule
|
|
117
|
+
cdef str filepath
|
|
118
|
+
cdef dict violation
|
|
119
|
+
|
|
120
|
+
for violation in data:
|
|
121
|
+
rule = violation.get("code", "UNKNOWN")
|
|
122
|
+
filepath = violation.get("filename", "unknown")
|
|
123
|
+
rule_counts[rule]["files"].add(filepath)
|
|
124
|
+
rule_counts[rule]["count"] += 1
|
|
125
|
+
|
|
126
|
+
parts: list[str] = []
|
|
127
|
+
for rule in sorted(rule_counts.keys()):
|
|
128
|
+
info = rule_counts[rule]
|
|
129
|
+
parts.append(f"{rule}: {len(info['files'])} files, {info['count']} occurrences")
|
|
130
|
+
if verbosity >= VerbosityLevel.VERBOSE:
|
|
131
|
+
for fp in sorted(info["files"])[:10]:
|
|
132
|
+
parts.append(f" {fp}")
|
|
133
|
+
if len(info["files"]) > 10:
|
|
134
|
+
parts.append(f" ... and {len(info['files']) - 10} more files")
|
|
135
|
+
|
|
136
|
+
return ShellCommandOutput(
|
|
137
|
+
success=len(data) == 0,
|
|
138
|
+
command="ruff",
|
|
139
|
+
stdout="\n".join(parts) if parts else "No issues found",
|
|
140
|
+
stderr=stderr if verbosity >= VerbosityLevel.VERY_VERBOSE else None,
|
|
141
|
+
exit_code=0 if not data else 1,
|
|
142
|
+
execution_time=None,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def compress_eslint(
|
|
147
|
+
stdout: str,
|
|
148
|
+
stderr: str,
|
|
149
|
+
verbosity: VerbosityLevel,
|
|
150
|
+
) -> ShellCommandOutput:
|
|
151
|
+
"""Compress eslint output by grouping rule/message type.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
stdout: Raw eslint stdout.
|
|
155
|
+
stderr: Raw eslint stderr.
|
|
156
|
+
verbosity: Current verbosity level.
|
|
157
|
+
|
|
158
|
+
Returns:
|
|
159
|
+
Compressed :class:`ShellCommandOutput`.
|
|
160
|
+
"""
|
|
161
|
+
# Try JSON first
|
|
162
|
+
try:
|
|
163
|
+
data = json.loads(stdout)
|
|
164
|
+
if isinstance(data, list):
|
|
165
|
+
return _compress_eslint_json(data, stderr, verbosity)
|
|
166
|
+
except ValueError:
|
|
167
|
+
pass
|
|
168
|
+
|
|
169
|
+
lines = stdout.splitlines()
|
|
170
|
+
rule_counts = defaultdict(lambda: {"files": set(), "count": 0})
|
|
171
|
+
|
|
172
|
+
# Pattern: file.js:5:3: error Some message [rule-id]
|
|
173
|
+
pattern = re.compile(r"^(.+?):\d+:\d+:\s*(error|warning|info)\s+(.*?)\s*\[(\S+)\]")
|
|
174
|
+
|
|
175
|
+
cdef str line
|
|
176
|
+
cdef object match
|
|
177
|
+
cdef str filepath
|
|
178
|
+
cdef str severity
|
|
179
|
+
cdef str rule
|
|
180
|
+
cdef str key
|
|
181
|
+
|
|
182
|
+
for line in lines:
|
|
183
|
+
match = pattern.match(line.rstrip("\r"))
|
|
184
|
+
if match:
|
|
185
|
+
filepath = match.group(1)
|
|
186
|
+
severity = match.group(2)
|
|
187
|
+
rule = match.group(4)
|
|
188
|
+
key = f"{severity}:{rule}"
|
|
189
|
+
rule_counts[key]["files"].add(filepath)
|
|
190
|
+
rule_counts[key]["count"] += 1
|
|
191
|
+
|
|
192
|
+
if not rule_counts:
|
|
193
|
+
return ShellCommandOutput(
|
|
194
|
+
success=True,
|
|
195
|
+
command="eslint",
|
|
196
|
+
stdout=stdout.strip() or "No issues found",
|
|
197
|
+
stderr=stderr if verbosity >= VerbosityLevel.VERY_VERBOSE else None,
|
|
198
|
+
exit_code=0,
|
|
199
|
+
execution_time=None,
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
parts: list[str] = []
|
|
203
|
+
for key in sorted(rule_counts.keys()):
|
|
204
|
+
info = rule_counts[key]
|
|
205
|
+
parts.append(f"{key}: {len(info['files'])} files, {info['count']} occurrences")
|
|
206
|
+
if verbosity >= VerbosityLevel.VERBOSE:
|
|
207
|
+
for fp in sorted(info["files"])[:10]:
|
|
208
|
+
parts.append(f" {fp}")
|
|
209
|
+
if len(info["files"]) > 10:
|
|
210
|
+
parts.append(f" ... and {len(info['files']) - 10} more files")
|
|
211
|
+
|
|
212
|
+
return ShellCommandOutput(
|
|
213
|
+
success="error:" not in stdout.lower(),
|
|
214
|
+
command="eslint",
|
|
215
|
+
stdout="\n".join(parts),
|
|
216
|
+
stderr=stderr if verbosity >= VerbosityLevel.VERY_VERBOSE else None,
|
|
217
|
+
exit_code=0,
|
|
218
|
+
execution_time=None,
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def _compress_eslint_json(
|
|
223
|
+
data: list[dict[str, Any]],
|
|
224
|
+
stderr: str,
|
|
225
|
+
verbosity: VerbosityLevel,
|
|
226
|
+
) -> ShellCommandOutput:
|
|
227
|
+
"""Compress eslint JSON output.
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
data: List of eslint result dicts.
|
|
231
|
+
stderr: Raw stderr.
|
|
232
|
+
verbosity: Current verbosity level.
|
|
233
|
+
|
|
234
|
+
Returns:
|
|
235
|
+
Compressed :class:`ShellCommandOutput`.
|
|
236
|
+
"""
|
|
237
|
+
rule_counts = defaultdict(lambda: {"files": set(), "count": 0})
|
|
238
|
+
|
|
239
|
+
cdef dict result
|
|
240
|
+
cdef str filepath
|
|
241
|
+
cdef dict msg
|
|
242
|
+
cdef int severity
|
|
243
|
+
cdef str rule
|
|
244
|
+
cdef str sev_str
|
|
245
|
+
cdef str key
|
|
246
|
+
|
|
247
|
+
for result in data:
|
|
248
|
+
filepath = result.get("filePath", "unknown")
|
|
249
|
+
for msg in result.get("messages", []):
|
|
250
|
+
severity = msg.get("severity", 0)
|
|
251
|
+
rule = msg.get("ruleId", "UNKNOWN")
|
|
252
|
+
sev_str = {1: "warning", 2: "error"}.get(severity, "info")
|
|
253
|
+
key = f"{sev_str}:{rule}"
|
|
254
|
+
rule_counts[key]["files"].add(filepath)
|
|
255
|
+
rule_counts[key]["count"] += 1
|
|
256
|
+
|
|
257
|
+
parts: list[str] = []
|
|
258
|
+
for key in sorted(rule_counts.keys()):
|
|
259
|
+
info = rule_counts[key]
|
|
260
|
+
parts.append(f"{key}: {len(info['files'])} files, {info['count']} occurrences")
|
|
261
|
+
|
|
262
|
+
return ShellCommandOutput(
|
|
263
|
+
success=not any(k.startswith("error:") for k in rule_counts),
|
|
264
|
+
command="eslint",
|
|
265
|
+
stdout="\n".join(parts) if parts else "No issues found",
|
|
266
|
+
stderr=stderr if verbosity >= VerbosityLevel.VERY_VERBOSE else None,
|
|
267
|
+
exit_code=0,
|
|
268
|
+
execution_time=None,
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def compress_golangci(
|
|
273
|
+
stdout: str,
|
|
274
|
+
stderr: str,
|
|
275
|
+
verbosity: VerbosityLevel,
|
|
276
|
+
) -> ShellCommandOutput:
|
|
277
|
+
"""Compress golangci-lint output.
|
|
278
|
+
|
|
279
|
+
Tries ``--out-format=json`` first, then falls back to text.
|
|
280
|
+
|
|
281
|
+
Args:
|
|
282
|
+
stdout: Raw stdout.
|
|
283
|
+
stderr: Raw stderr.
|
|
284
|
+
verbosity: Current verbosity level.
|
|
285
|
+
|
|
286
|
+
Returns:
|
|
287
|
+
Compressed :class:`ShellCommandOutput`.
|
|
288
|
+
"""
|
|
289
|
+
# Try JSON first
|
|
290
|
+
try:
|
|
291
|
+
data = json.loads(stdout)
|
|
292
|
+
if isinstance(data, dict) and "Issues" in data:
|
|
293
|
+
return _compress_golangci_json(data, stderr, verbosity)
|
|
294
|
+
except ValueError:
|
|
295
|
+
pass
|
|
296
|
+
|
|
297
|
+
lines = stdout.splitlines()
|
|
298
|
+
rule_counts = defaultdict(lambda: {"files": set(), "count": 0})
|
|
299
|
+
|
|
300
|
+
# Pattern: file.go:5:3: message (rule-id)
|
|
301
|
+
pattern = re.compile(r"^(.+?):\d+:\d+:\s*(.*)")
|
|
302
|
+
|
|
303
|
+
cdef str line
|
|
304
|
+
cdef object match
|
|
305
|
+
cdef str filepath
|
|
306
|
+
cdef str rest
|
|
307
|
+
cdef object rule_match
|
|
308
|
+
cdef str rule
|
|
309
|
+
|
|
310
|
+
for line in lines:
|
|
311
|
+
match = pattern.match(line.rstrip("\r"))
|
|
312
|
+
if match:
|
|
313
|
+
filepath = match.group(1)
|
|
314
|
+
rest = match.group(2)
|
|
315
|
+
# Extract rule-id from parentheses if present
|
|
316
|
+
rule_match = re.search(r"\((\S+)\)$", rest)
|
|
317
|
+
if rule_match:
|
|
318
|
+
rule = rule_match.group(1)
|
|
319
|
+
else:
|
|
320
|
+
rule = rest.split()[0] if rest.split() else "UNKNOWN"
|
|
321
|
+
rule_counts[rule]["files"].add(filepath)
|
|
322
|
+
rule_counts[rule]["count"] += 1
|
|
323
|
+
|
|
324
|
+
if not rule_counts:
|
|
325
|
+
return ShellCommandOutput(
|
|
326
|
+
success=True,
|
|
327
|
+
command="golangci-lint",
|
|
328
|
+
stdout=stdout.strip() or "No issues found",
|
|
329
|
+
stderr=stderr if verbosity >= VerbosityLevel.VERY_VERBOSE else None,
|
|
330
|
+
exit_code=0,
|
|
331
|
+
execution_time=None,
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
parts: list[str] = []
|
|
335
|
+
for rule in sorted(rule_counts.keys()):
|
|
336
|
+
info = rule_counts[rule]
|
|
337
|
+
parts.append(f"{rule}: {len(info['files'])} files, {info['count']} occurrences")
|
|
338
|
+
if verbosity >= VerbosityLevel.VERBOSE:
|
|
339
|
+
for fp in sorted(info["files"])[:10]:
|
|
340
|
+
parts.append(f" {fp}")
|
|
341
|
+
if len(info["files"]) > 10:
|
|
342
|
+
parts.append(f" ... and {len(info['files']) - 10} more files")
|
|
343
|
+
|
|
344
|
+
return ShellCommandOutput(
|
|
345
|
+
success=True,
|
|
346
|
+
command="golangci-lint",
|
|
347
|
+
stdout="\n".join(parts),
|
|
348
|
+
stderr=stderr if verbosity >= VerbosityLevel.VERY_VERBOSE else None,
|
|
349
|
+
exit_code=0,
|
|
350
|
+
execution_time=None,
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
def _compress_golangci_json(
|
|
355
|
+
data: dict[str, Any],
|
|
356
|
+
stderr: str,
|
|
357
|
+
verbosity: VerbosityLevel,
|
|
358
|
+
) -> ShellCommandOutput:
|
|
359
|
+
"""Compress golangci-lint JSON output.
|
|
360
|
+
|
|
361
|
+
Args:
|
|
362
|
+
data: Parsed golangci-lint JSON.
|
|
363
|
+
stderr: Raw stderr.
|
|
364
|
+
verbosity: Current verbosity level.
|
|
365
|
+
|
|
366
|
+
Returns:
|
|
367
|
+
Compressed :class:`ShellCommandOutput`.
|
|
368
|
+
"""
|
|
369
|
+
issues = data.get("Issues", [])
|
|
370
|
+
rule_counts = defaultdict(lambda: {"files": set(), "count": 0})
|
|
371
|
+
|
|
372
|
+
cdef dict issue
|
|
373
|
+
cdef str rule
|
|
374
|
+
cdef str filepath
|
|
375
|
+
|
|
376
|
+
for issue in issues:
|
|
377
|
+
rule = issue.get("FromLinter", "UNKNOWN")
|
|
378
|
+
filepath = issue.get("Pos", {}).get("Filename", "unknown")
|
|
379
|
+
rule_counts[rule]["files"].add(filepath)
|
|
380
|
+
rule_counts[rule]["count"] += 1
|
|
381
|
+
|
|
382
|
+
parts: list[str] = []
|
|
383
|
+
for rule in sorted(rule_counts.keys()):
|
|
384
|
+
info = rule_counts[rule]
|
|
385
|
+
parts.append(f"{rule}: {len(info['files'])} files, {info['count']} occurrences")
|
|
386
|
+
|
|
387
|
+
return ShellCommandOutput(
|
|
388
|
+
success=len(issues) == 0,
|
|
389
|
+
command="golangci-lint",
|
|
390
|
+
stdout="\n".join(parts) if parts else "No issues found",
|
|
391
|
+
stderr=stderr if verbosity >= VerbosityLevel.VERY_VERBOSE else None,
|
|
392
|
+
exit_code=0 if not issues else 1,
|
|
393
|
+
execution_time=None,
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
def compress_grep(
|
|
398
|
+
stdout: str,
|
|
399
|
+
stderr: str,
|
|
400
|
+
verbosity: VerbosityLevel,
|
|
401
|
+
) -> ShellCommandOutput:
|
|
402
|
+
"""Compress grep/rg output by grouping matches per file.
|
|
403
|
+
|
|
404
|
+
Args:
|
|
405
|
+
stdout: Raw stdout.
|
|
406
|
+
stderr: Raw stderr.
|
|
407
|
+
verbosity: Current verbosity level.
|
|
408
|
+
|
|
409
|
+
Returns:
|
|
410
|
+
Compressed :class:`ShellCommandOutput`.
|
|
411
|
+
"""
|
|
412
|
+
lines = stdout.splitlines()
|
|
413
|
+
file_lines = defaultdict(list)
|
|
414
|
+
|
|
415
|
+
cdef str line
|
|
416
|
+
cdef str stripped
|
|
417
|
+
cdef list parts
|
|
418
|
+
cdef str filepath
|
|
419
|
+
cdef str rest
|
|
420
|
+
|
|
421
|
+
# Pattern: file:line:match or file:match
|
|
422
|
+
for line in lines:
|
|
423
|
+
stripped = line.rstrip("\r")
|
|
424
|
+
if ":" in stripped:
|
|
425
|
+
parts = stripped.split(":", 2)
|
|
426
|
+
if len(parts) >= 2 and parts[0]:
|
|
427
|
+
filepath = parts[0]
|
|
428
|
+
rest = ":".join(parts[1:])
|
|
429
|
+
file_lines[filepath].append(rest)
|
|
430
|
+
else:
|
|
431
|
+
file_lines[""].append(stripped)
|
|
432
|
+
else:
|
|
433
|
+
file_lines[""].append(stripped)
|
|
434
|
+
|
|
435
|
+
if not file_lines:
|
|
436
|
+
return ShellCommandOutput(
|
|
437
|
+
success=True,
|
|
438
|
+
command="grep",
|
|
439
|
+
stdout=stdout.strip() or "No matches",
|
|
440
|
+
stderr=stderr if verbosity >= VerbosityLevel.VERY_VERBOSE else None,
|
|
441
|
+
exit_code=0,
|
|
442
|
+
execution_time=None,
|
|
443
|
+
)
|
|
444
|
+
|
|
445
|
+
parts: list[str] = []
|
|
446
|
+
for filepath in sorted(file_lines.keys()):
|
|
447
|
+
matches = file_lines[filepath]
|
|
448
|
+
if filepath:
|
|
449
|
+
parts.append(f"{filepath}: {len(matches)} matches")
|
|
450
|
+
if verbosity >= VerbosityLevel.VERBOSE:
|
|
451
|
+
for m in matches[:5]:
|
|
452
|
+
parts.append(f" {m}")
|
|
453
|
+
if len(matches) > 5:
|
|
454
|
+
parts.append(f" ... {len(matches) - 5} more")
|
|
455
|
+
else:
|
|
456
|
+
parts.extend(matches[:10])
|
|
457
|
+
if len(matches) > 10:
|
|
458
|
+
parts.append(f"... {len(matches) - 10} more")
|
|
459
|
+
|
|
460
|
+
return ShellCommandOutput(
|
|
461
|
+
success=True,
|
|
462
|
+
command="grep",
|
|
463
|
+
stdout="\n".join(parts),
|
|
464
|
+
stderr=stderr if verbosity >= VerbosityLevel.VERY_VERBOSE else None,
|
|
465
|
+
exit_code=0,
|
|
466
|
+
execution_time=None,
|
|
467
|
+
)
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
def compress_find(
|
|
471
|
+
stdout: str,
|
|
472
|
+
stderr: str,
|
|
473
|
+
verbosity: VerbosityLevel,
|
|
474
|
+
) -> ShellCommandOutput:
|
|
475
|
+
"""Compress find output by grouping results per directory.
|
|
476
|
+
|
|
477
|
+
Args:
|
|
478
|
+
stdout: Raw stdout.
|
|
479
|
+
stderr: Raw stderr.
|
|
480
|
+
verbosity: Current verbosity level.
|
|
481
|
+
|
|
482
|
+
Returns:
|
|
483
|
+
Compressed :class:`ShellCommandOutput`.
|
|
484
|
+
"""
|
|
485
|
+
lines = stdout.splitlines()
|
|
486
|
+
dir_counts = defaultdict(lambda: {"files": 0, "dirs": 0})
|
|
487
|
+
|
|
488
|
+
cdef str line
|
|
489
|
+
cdef str stripped
|
|
490
|
+
cdef str directory
|
|
491
|
+
|
|
492
|
+
for line in lines:
|
|
493
|
+
stripped = line.rstrip("\r")
|
|
494
|
+
if not stripped:
|
|
495
|
+
continue
|
|
496
|
+
directory = stripped.rsplit("/", 1)[0] if "/" in stripped else "."
|
|
497
|
+
# Heuristic: trailing slash indicates directory
|
|
498
|
+
if stripped.endswith("/"):
|
|
499
|
+
dir_counts[directory]["dirs"] += 1
|
|
500
|
+
else:
|
|
501
|
+
dir_counts[directory]["files"] += 1
|
|
502
|
+
|
|
503
|
+
if not dir_counts:
|
|
504
|
+
return ShellCommandOutput(
|
|
505
|
+
success=True,
|
|
506
|
+
command="find",
|
|
507
|
+
stdout=stdout.strip() or "No results",
|
|
508
|
+
stderr=stderr if verbosity >= VerbosityLevel.VERY_VERBOSE else None,
|
|
509
|
+
exit_code=0,
|
|
510
|
+
execution_time=None,
|
|
511
|
+
)
|
|
512
|
+
|
|
513
|
+
parts: list[str] = []
|
|
514
|
+
for directory in sorted(dir_counts.keys()):
|
|
515
|
+
counts = dir_counts[directory]
|
|
516
|
+
parts.append(f"{directory}: {counts['files']} files, {counts['dirs']} dirs")
|
|
517
|
+
if verbosity >= VerbosityLevel.VERBOSE:
|
|
518
|
+
# Show a few sample entries for this directory
|
|
519
|
+
samples = [line for line in lines if line.startswith(directory + "/")][:5]
|
|
520
|
+
for s in samples:
|
|
521
|
+
parts.append(f" {s.split('/')[-1]}")
|
|
522
|
+
|
|
523
|
+
return ShellCommandOutput(
|
|
524
|
+
success=True,
|
|
525
|
+
command="find",
|
|
526
|
+
stdout="\n".join(parts),
|
|
527
|
+
stderr=stderr if verbosity >= VerbosityLevel.VERY_VERBOSE else None,
|
|
528
|
+
exit_code=0,
|
|
529
|
+
execution_time=None,
|
|
530
|
+
)
|
|
531
|
+
|
|
532
|
+
|
|
533
|
+
_COMMAND_HANDLERS: dict[str, Any] = {
|
|
534
|
+
"ruff": compress_ruff,
|
|
535
|
+
"eslint": compress_eslint,
|
|
536
|
+
"golangci-lint": compress_golangci,
|
|
537
|
+
"grep": compress_grep,
|
|
538
|
+
"rg": compress_grep,
|
|
539
|
+
"find": compress_find,
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
|
|
543
|
+
def _resolve_handler(command: str) -> Any:
|
|
544
|
+
cdef str token
|
|
545
|
+
cdef str name
|
|
546
|
+
cdef object handler
|
|
547
|
+
|
|
548
|
+
for token in command.strip().split():
|
|
549
|
+
name = token.rsplit("/", 1)[-1]
|
|
550
|
+
handler = _COMMAND_HANDLERS.get(name)
|
|
551
|
+
if handler is not None:
|
|
552
|
+
return handler
|
|
553
|
+
return None
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
def compress_lint(
|
|
557
|
+
command: str,
|
|
558
|
+
stdout: str,
|
|
559
|
+
stderr: str,
|
|
560
|
+
exit_code: int,
|
|
561
|
+
verbosity: VerbosityLevel,
|
|
562
|
+
) -> ShellCommandOutput | None:
|
|
563
|
+
"""Main dispatcher for lint compression strategies.
|
|
564
|
+
|
|
565
|
+
Args:
|
|
566
|
+
command: The original lint command.
|
|
567
|
+
stdout: Raw stdout.
|
|
568
|
+
stderr: Raw stderr.
|
|
569
|
+
exit_code: Process exit code.
|
|
570
|
+
verbosity: Current verbosity level.
|
|
571
|
+
|
|
572
|
+
Returns:
|
|
573
|
+
A compressed :class:`ShellCommandOutput` or ``None``.
|
|
574
|
+
"""
|
|
575
|
+
cdef object handler
|
|
576
|
+
|
|
577
|
+
handler = _resolve_handler(command)
|
|
578
|
+
if handler is not None:
|
|
579
|
+
return handler(stdout, stderr, verbosity)
|
|
580
|
+
|
|
581
|
+
# Generic fallback: group by first token on each line that looks like a rule
|
|
582
|
+
lines = stdout.splitlines()
|
|
583
|
+
parts: list[str] = []
|
|
584
|
+
|
|
585
|
+
cdef str line
|
|
586
|
+
cdef str stripped_line
|
|
587
|
+
cdef object rule_match
|
|
588
|
+
cdef str key
|
|
589
|
+
|
|
590
|
+
if verbosity >= VerbosityLevel.VERBOSE:
|
|
591
|
+
groups = defaultdict(list)
|
|
592
|
+
for line in lines:
|
|
593
|
+
stripped_line = line.rstrip("\r")
|
|
594
|
+
rule_match = _RULE_PATTERN.search(stripped_line)
|
|
595
|
+
key = rule_match.group(1) if rule_match else "other"
|
|
596
|
+
groups[key].append(stripped_line)
|
|
597
|
+
|
|
598
|
+
for key in sorted(groups.keys()):
|
|
599
|
+
parts.append(f"{key}: {len(groups[key])} occurrences")
|
|
600
|
+
for line in groups[key][:3]:
|
|
601
|
+
parts.append(f" {line}")
|
|
602
|
+
else:
|
|
603
|
+
counts: dict[str, int] = {}
|
|
604
|
+
for line in lines:
|
|
605
|
+
stripped_line = line.rstrip("\r")
|
|
606
|
+
rule_match = _RULE_PATTERN.search(stripped_line)
|
|
607
|
+
key = rule_match.group(1) if rule_match else "other"
|
|
608
|
+
counts[key] = counts.get(key, 0) + 1
|
|
609
|
+
|
|
610
|
+
for key, count in sorted(counts.items()):
|
|
611
|
+
parts.append(f"{key}: {count} occurrences")
|
|
612
|
+
|
|
613
|
+
return ShellCommandOutput(
|
|
614
|
+
success=exit_code == 0,
|
|
615
|
+
command=command.strip(),
|
|
616
|
+
stdout="\n".join(parts) if parts else stdout[:512],
|
|
617
|
+
stderr=stderr if verbosity >= VerbosityLevel.VERY_VERBOSE else None,
|
|
618
|
+
exit_code=exit_code,
|
|
619
|
+
execution_time=None,
|
|
620
|
+
)
|
|
621
|
+
|
|
622
|
+
|
|
623
|
+
# ---------------------------------------------------------------------------
|
|
624
|
+
# Register with the strategy registry
|
|
625
|
+
# ---------------------------------------------------------------------------
|
|
626
|
+
get_registry().register("lint", compress_lint, priority=0)
|
|
Binary file
|