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,154 @@
|
|
|
1
|
+
"""Time-synchronized shimmer / sparkle text effect.
|
|
2
|
+
|
|
3
|
+
Port of the Codex `shimmer_spans` effect (Rust/ratatui) to Python/Rich.
|
|
4
|
+
Produces a cosine-based highlight band that sweeps across text characters
|
|
5
|
+
so all shimmer text on screen animates in lockstep.
|
|
6
|
+
|
|
7
|
+
Works with truecolor (RGB blending) and degrades gracefully on terminals
|
|
8
|
+
without truecolor support (uses DIM/BOLD modifiers).
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import math
|
|
12
|
+
import time
|
|
13
|
+
|
|
14
|
+
from rich.style import Style
|
|
15
|
+
from rich.text import Span
|
|
16
|
+
|
|
17
|
+
from code_muse.terminal_utils import detect_truecolor_support
|
|
18
|
+
|
|
19
|
+
# ---------------------------------------------------------------------------
|
|
20
|
+
# Process-start anchor so every shimmer instance is synchronized
|
|
21
|
+
# ---------------------------------------------------------------------------
|
|
22
|
+
|
|
23
|
+
_process_start: float | None = None
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _elapsed_since_start() -> float:
|
|
27
|
+
"""Seconds since the first shimmer call in this process."""
|
|
28
|
+
global _process_start
|
|
29
|
+
if _process_start is None:
|
|
30
|
+
_process_start = time.monotonic()
|
|
31
|
+
return time.monotonic() - _process_start
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
# ---------------------------------------------------------------------------
|
|
35
|
+
# RGB helpers
|
|
36
|
+
# ---------------------------------------------------------------------------
|
|
37
|
+
|
|
38
|
+
RGB = tuple[int, int, int]
|
|
39
|
+
|
|
40
|
+
# Sensible defaults for terminals whose true fg/bg we cannot query.
|
|
41
|
+
_DEFAULT_BASE: RGB = (160, 160, 160) # medium gray — typical dimmed foreground
|
|
42
|
+
_DEFAULT_HIGHLIGHT: RGB = (255, 255, 255) # white — background highlight
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def blend(fg: RGB, bg: RGB, alpha: float) -> RGB:
|
|
46
|
+
"""Linear alpha-blend between two RGB colours.
|
|
47
|
+
|
|
48
|
+
Returns components clamped to 0..255.
|
|
49
|
+
"""
|
|
50
|
+
inv = 1.0 - alpha
|
|
51
|
+
return (
|
|
52
|
+
min(255, max(0, int(fg[0] * alpha + bg[0] * inv))),
|
|
53
|
+
min(255, max(0, int(fg[1] * alpha + bg[1] * inv))),
|
|
54
|
+
min(255, max(0, int(fg[2] * alpha + bg[2] * inv))),
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
# ---------------------------------------------------------------------------
|
|
59
|
+
# Shimmer spans
|
|
60
|
+
# ---------------------------------------------------------------------------
|
|
61
|
+
|
|
62
|
+
# Cosine half-width of the highlight band in character positions.
|
|
63
|
+
_BAND_HALF_WIDTH = 5.0
|
|
64
|
+
|
|
65
|
+
# Padding added to each side of the text so the band can enter/exit smoothly.
|
|
66
|
+
_PADDING = 10
|
|
67
|
+
|
|
68
|
+
# Period of the sweep in seconds.
|
|
69
|
+
_SWEEP_SECONDS = 2.0
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def shimmer_spans(
|
|
73
|
+
text: str,
|
|
74
|
+
*,
|
|
75
|
+
base_color: RGB | None = None,
|
|
76
|
+
highlight_color: RGB | None = None,
|
|
77
|
+
) -> list[Span]:
|
|
78
|
+
"""Return a list of Rich ``Span`` objects with a shimmer highlight band.
|
|
79
|
+
|
|
80
|
+
The highlight sweeps across *text* once every ``_SWEEP_SECONDS`` seconds,
|
|
81
|
+
synchronized to the first call anywhere in the process so multiple
|
|
82
|
+
shimmer texts animate in lockstep.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
text: The string to shimmer-ify.
|
|
86
|
+
base_color: RGB tuple for dimmed characters. Defaults to
|
|
87
|
+
``_DEFAULT_BASE``.
|
|
88
|
+
highlight_color: RGB tuple for the highlight peak. Defaults to
|
|
89
|
+
``_DEFAULT_HIGHLIGHT``.
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
List of ``Span`` objects styled appropriately.
|
|
93
|
+
"""
|
|
94
|
+
chars = list(text)
|
|
95
|
+
if not chars:
|
|
96
|
+
return []
|
|
97
|
+
|
|
98
|
+
base = base_color or _DEFAULT_BASE
|
|
99
|
+
highlight = highlight_color or _DEFAULT_HIGHLIGHT
|
|
100
|
+
|
|
101
|
+
has_truecolor = detect_truecolor_support()
|
|
102
|
+
|
|
103
|
+
period = len(chars) + _PADDING * 2
|
|
104
|
+
elapsed = _elapsed_since_start()
|
|
105
|
+
# Fractional position of the band centre (0 .. period).
|
|
106
|
+
pos_f = (elapsed % _SWEEP_SECONDS) / _SWEEP_SECONDS * float(period)
|
|
107
|
+
|
|
108
|
+
spans: list[Span] = []
|
|
109
|
+
|
|
110
|
+
for i, _ch in enumerate(chars):
|
|
111
|
+
# Signed distance from band centre (padding-adjusted).
|
|
112
|
+
i_pos = float(i + _PADDING)
|
|
113
|
+
dist = abs(i_pos - pos_f)
|
|
114
|
+
|
|
115
|
+
# Cosine intensity: 1.0 at the centre, 0.0 beyond half-width.
|
|
116
|
+
if dist <= _BAND_HALF_WIDTH:
|
|
117
|
+
x = math.pi * (dist / _BAND_HALF_WIDTH)
|
|
118
|
+
t = 0.5 * (1.0 + math.cos(x))
|
|
119
|
+
else:
|
|
120
|
+
t = 0.0
|
|
121
|
+
|
|
122
|
+
if has_truecolor:
|
|
123
|
+
alpha = t * 0.9 # peak blend strength
|
|
124
|
+
r, g, b = blend(highlight, base, alpha)
|
|
125
|
+
from rich.color import Color
|
|
126
|
+
|
|
127
|
+
style = Style(color=Color.from_rgb(r, g, b), bold=True)
|
|
128
|
+
else:
|
|
129
|
+
style = _color_for_level(t)
|
|
130
|
+
|
|
131
|
+
spans.append(Span(i, i + 1, style))
|
|
132
|
+
|
|
133
|
+
return spans
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
# ---------------------------------------------------------------------------
|
|
137
|
+
# Fallback for terminals without truecolor
|
|
138
|
+
# ---------------------------------------------------------------------------
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _color_for_level(intensity: float) -> Style:
|
|
142
|
+
"""Map a cosine intensity to a Rich Style using modifiers only.
|
|
143
|
+
|
|
144
|
+
This keeps the shimmer readable on basic terminals:
|
|
145
|
+
- < 0.2 → DIM (faint)
|
|
146
|
+
- 0.2–0.6 → NORMAL
|
|
147
|
+
- > 0.6 → BOLD (bright)
|
|
148
|
+
"""
|
|
149
|
+
if intensity < 0.2:
|
|
150
|
+
return Style(dim=True)
|
|
151
|
+
elif intensity < 0.6:
|
|
152
|
+
return Style()
|
|
153
|
+
else:
|
|
154
|
+
return Style(bold=True)
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Shared spinner implementation for CLI mode.
|
|
3
|
+
|
|
4
|
+
This module provides consistent spinner animations across different UI modes.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from rich.spinner import SPINNERS
|
|
8
|
+
|
|
9
|
+
from .console_spinner import ConsoleSpinner
|
|
10
|
+
from .spinner_base import SpinnerBase
|
|
11
|
+
|
|
12
|
+
SPINNERS["radar"] = {"interval": 80, "frames": ["◝", "◜", "◠", "◡"]}
|
|
13
|
+
|
|
14
|
+
# Keep track of all active spinners to manage them globally
|
|
15
|
+
_active_spinners = []
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def register_spinner(spinner):
|
|
19
|
+
"""Register an active spinner to be managed globally."""
|
|
20
|
+
if spinner not in _active_spinners:
|
|
21
|
+
_active_spinners.append(spinner)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def unregister_spinner(spinner):
|
|
25
|
+
"""Remove a spinner from global management."""
|
|
26
|
+
if spinner in _active_spinners:
|
|
27
|
+
_active_spinners.remove(spinner)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def pause_all_spinners():
|
|
31
|
+
"""Pause all active spinners.
|
|
32
|
+
|
|
33
|
+
No-op when called from a sub-agent context to prevent
|
|
34
|
+
parallel sub-agents from interfering with the main spinner.
|
|
35
|
+
"""
|
|
36
|
+
# Lazy import to avoid circular dependency
|
|
37
|
+
from code_muse.tools.subagent_context import is_subagent
|
|
38
|
+
|
|
39
|
+
if is_subagent():
|
|
40
|
+
return # Sub-agents don't control the main spinner
|
|
41
|
+
for spinner in _active_spinners:
|
|
42
|
+
try:
|
|
43
|
+
spinner.pause()
|
|
44
|
+
except Exception:
|
|
45
|
+
# Ignore errors if a spinner can't be paused
|
|
46
|
+
pass
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def resume_all_spinners():
|
|
50
|
+
"""Resume all active spinners.
|
|
51
|
+
|
|
52
|
+
No-op when called from a sub-agent context to prevent
|
|
53
|
+
parallel sub-agents from interfering with the main spinner.
|
|
54
|
+
"""
|
|
55
|
+
# Lazy import to avoid circular dependency
|
|
56
|
+
from code_muse.tools.subagent_context import is_subagent
|
|
57
|
+
|
|
58
|
+
if is_subagent():
|
|
59
|
+
return # Sub-agents don't control the main spinner
|
|
60
|
+
for spinner in _active_spinners:
|
|
61
|
+
try:
|
|
62
|
+
spinner.resume()
|
|
63
|
+
except Exception:
|
|
64
|
+
# Ignore errors if a spinner can't be resumed
|
|
65
|
+
pass
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def update_spinner_context(info: str) -> None:
|
|
69
|
+
"""Update the shared context information displayed beside active spinners."""
|
|
70
|
+
SpinnerBase.set_context_info(info)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def clear_spinner_context() -> None:
|
|
74
|
+
"""Clear any context information displayed beside active spinners."""
|
|
75
|
+
SpinnerBase.clear_context_info()
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
__all__ = [
|
|
79
|
+
"SpinnerBase",
|
|
80
|
+
"ConsoleSpinner",
|
|
81
|
+
"register_spinner",
|
|
82
|
+
"unregister_spinner",
|
|
83
|
+
"pause_all_spinners",
|
|
84
|
+
"resume_all_spinners",
|
|
85
|
+
"update_spinner_context",
|
|
86
|
+
"clear_spinner_context",
|
|
87
|
+
]
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Console spinner implementation for CLI mode using Rich's Live Display.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import platform
|
|
6
|
+
import threading
|
|
7
|
+
import time
|
|
8
|
+
|
|
9
|
+
from rich.console import Console
|
|
10
|
+
from rich.live import Live
|
|
11
|
+
from rich.text import Text
|
|
12
|
+
|
|
13
|
+
from code_muse.config import get_animations_enabled
|
|
14
|
+
from code_muse.motion import MotionMode, shimmer_text
|
|
15
|
+
|
|
16
|
+
from .spinner_base import SpinnerBase
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ConsoleSpinner(SpinnerBase):
|
|
20
|
+
"""A console-based spinner implementation using Rich's Live Display."""
|
|
21
|
+
|
|
22
|
+
def __init__(self, console=None):
|
|
23
|
+
"""Initialize the console spinner.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
console: Optional Rich console instance to use for output.
|
|
27
|
+
If not provided, a new one will be created.
|
|
28
|
+
"""
|
|
29
|
+
super().__init__()
|
|
30
|
+
self.console = console or Console()
|
|
31
|
+
self._thread = None
|
|
32
|
+
self._stop_event = threading.Event()
|
|
33
|
+
self._paused = False
|
|
34
|
+
self._live = None
|
|
35
|
+
self._animations_enabled = get_animations_enabled()
|
|
36
|
+
|
|
37
|
+
# Register this spinner for global management
|
|
38
|
+
from . import register_spinner
|
|
39
|
+
|
|
40
|
+
register_spinner(self)
|
|
41
|
+
|
|
42
|
+
def start(self):
|
|
43
|
+
"""Start the spinner animation."""
|
|
44
|
+
super().start()
|
|
45
|
+
self._stop_event.clear()
|
|
46
|
+
|
|
47
|
+
# Don't start a new thread if one is already running
|
|
48
|
+
if self._thread and self._thread.is_alive():
|
|
49
|
+
return
|
|
50
|
+
|
|
51
|
+
# Print blank line before spinner for visual separation from content
|
|
52
|
+
self.console.print()
|
|
53
|
+
|
|
54
|
+
# Create a Live display for the spinner
|
|
55
|
+
self._live = Live(
|
|
56
|
+
self._generate_spinner_panel(),
|
|
57
|
+
console=self.console,
|
|
58
|
+
refresh_per_second=8,
|
|
59
|
+
transient=True, # Clear the spinner line when stopped (no residue left!)
|
|
60
|
+
auto_refresh=False, # Don't auto-refresh to avoid wiping out user input
|
|
61
|
+
)
|
|
62
|
+
self._live.start()
|
|
63
|
+
|
|
64
|
+
# Start a thread to update the spinner frames
|
|
65
|
+
self._thread = threading.Thread(target=self._update_spinner)
|
|
66
|
+
self._thread.daemon = True
|
|
67
|
+
self._thread.start()
|
|
68
|
+
|
|
69
|
+
def stop(self):
|
|
70
|
+
"""Stop the spinner animation."""
|
|
71
|
+
if not self._is_spinning:
|
|
72
|
+
return
|
|
73
|
+
|
|
74
|
+
self._stop_event.set()
|
|
75
|
+
self._is_spinning = False
|
|
76
|
+
|
|
77
|
+
if self._live:
|
|
78
|
+
self._live.stop()
|
|
79
|
+
self._live = None
|
|
80
|
+
|
|
81
|
+
if self._thread and self._thread.is_alive():
|
|
82
|
+
self._thread.join(timeout=0.5)
|
|
83
|
+
|
|
84
|
+
self._thread = None
|
|
85
|
+
|
|
86
|
+
# Windows-specific cleanup: Rich's Live display can leave terminal in corrupted state
|
|
87
|
+
if platform.system() == "Windows":
|
|
88
|
+
import sys
|
|
89
|
+
|
|
90
|
+
try:
|
|
91
|
+
# Reset ANSI formatting for both stdout and stderr
|
|
92
|
+
sys.stdout.write("\x1b[0m") # Reset all attributes
|
|
93
|
+
sys.stdout.flush()
|
|
94
|
+
sys.stderr.write("\x1b[0m")
|
|
95
|
+
sys.stderr.flush()
|
|
96
|
+
|
|
97
|
+
# Clear the line and reposition cursor
|
|
98
|
+
sys.stdout.write("\r") # Return to start of line
|
|
99
|
+
sys.stdout.write("\x1b[K") # Clear to end of line
|
|
100
|
+
sys.stdout.flush()
|
|
101
|
+
|
|
102
|
+
# Flush keyboard input buffer to clear any stuck keys
|
|
103
|
+
try:
|
|
104
|
+
import msvcrt
|
|
105
|
+
|
|
106
|
+
while msvcrt.kbhit():
|
|
107
|
+
msvcrt.getch()
|
|
108
|
+
except ImportError:
|
|
109
|
+
pass # msvcrt not available (not Windows or different Python impl)
|
|
110
|
+
except Exception:
|
|
111
|
+
pass # Fail silently if cleanup doesn't work
|
|
112
|
+
|
|
113
|
+
# Unregister this spinner from global management
|
|
114
|
+
from . import unregister_spinner
|
|
115
|
+
|
|
116
|
+
unregister_spinner(self)
|
|
117
|
+
|
|
118
|
+
def update_frame(self):
|
|
119
|
+
"""Update to the next frame."""
|
|
120
|
+
super().update_frame()
|
|
121
|
+
|
|
122
|
+
def _generate_spinner_panel(self):
|
|
123
|
+
"""Generate a Rich panel containing the spinner text."""
|
|
124
|
+
from code_muse.tools.command_runner import is_awaiting_user_input
|
|
125
|
+
|
|
126
|
+
if self._paused or is_awaiting_user_input():
|
|
127
|
+
return Text("")
|
|
128
|
+
|
|
129
|
+
text = Text()
|
|
130
|
+
|
|
131
|
+
motion_mode = MotionMode.from_animations_enabled(self._animations_enabled)
|
|
132
|
+
|
|
133
|
+
if motion_mode == MotionMode.ANIMATED:
|
|
134
|
+
# Use shimmer text — the highlight band sweeps across "Thinking..."
|
|
135
|
+
thinking = Text("Thinking...")
|
|
136
|
+
thinking.spans = shimmer_text("Thinking...", motion_mode)
|
|
137
|
+
text.append(thinking)
|
|
138
|
+
else:
|
|
139
|
+
# Reduced motion: classic spinner frame
|
|
140
|
+
text.append(SpinnerBase.THINKING_MESSAGE, style="bold green")
|
|
141
|
+
text.append(self.current_frame, style="bold green")
|
|
142
|
+
|
|
143
|
+
context_info = SpinnerBase.get_context_info()
|
|
144
|
+
if context_info:
|
|
145
|
+
text.append(" ")
|
|
146
|
+
text.append(context_info, style="bold white")
|
|
147
|
+
|
|
148
|
+
return text
|
|
149
|
+
|
|
150
|
+
def _update_spinner(self):
|
|
151
|
+
"""Update the spinner in a background thread."""
|
|
152
|
+
try:
|
|
153
|
+
while not self._stop_event.is_set():
|
|
154
|
+
# Update the frame
|
|
155
|
+
self.update_frame()
|
|
156
|
+
|
|
157
|
+
# Check if we're awaiting user input before updating the display
|
|
158
|
+
from code_muse.tools.command_runner import is_awaiting_user_input
|
|
159
|
+
|
|
160
|
+
awaiting_input = is_awaiting_user_input()
|
|
161
|
+
|
|
162
|
+
# Update the live display only if not paused and not awaiting input
|
|
163
|
+
if self._live and not self._paused and not awaiting_input:
|
|
164
|
+
# Manually refresh instead of auto-refresh to avoid wiping input
|
|
165
|
+
self._live.update(self._generate_spinner_panel())
|
|
166
|
+
self._live.refresh()
|
|
167
|
+
|
|
168
|
+
# Short sleep to control animation speed
|
|
169
|
+
time.sleep(0.12)
|
|
170
|
+
except Exception as e:
|
|
171
|
+
# Note: Using sys.stderr - can't use messaging during spinner
|
|
172
|
+
import sys
|
|
173
|
+
|
|
174
|
+
sys.stderr.write(f"\nSpinner error: {e}\n")
|
|
175
|
+
self._is_spinning = False
|
|
176
|
+
|
|
177
|
+
def pause(self):
|
|
178
|
+
"""Pause the spinner animation."""
|
|
179
|
+
if self._is_spinning:
|
|
180
|
+
self._paused = True
|
|
181
|
+
# Stop the live display completely to restore terminal echo during input
|
|
182
|
+
if self._live:
|
|
183
|
+
try:
|
|
184
|
+
self._live.stop()
|
|
185
|
+
self._live = None
|
|
186
|
+
# Clear the line to remove any artifacts
|
|
187
|
+
import sys
|
|
188
|
+
|
|
189
|
+
sys.stdout.write("\r") # Return to start of line
|
|
190
|
+
sys.stdout.write("\x1b[K") # Clear to end of line
|
|
191
|
+
sys.stdout.flush()
|
|
192
|
+
except Exception:
|
|
193
|
+
pass
|
|
194
|
+
|
|
195
|
+
def resume(self):
|
|
196
|
+
"""Resume the spinner animation."""
|
|
197
|
+
# Check if we should show a spinner - don't resume if waiting for user input
|
|
198
|
+
from code_muse.tools.command_runner import is_awaiting_user_input
|
|
199
|
+
|
|
200
|
+
if is_awaiting_user_input():
|
|
201
|
+
return # Don't resume if waiting for user input
|
|
202
|
+
|
|
203
|
+
if self._is_spinning and self._paused:
|
|
204
|
+
self._paused = False
|
|
205
|
+
# Restart the live display if it was stopped during pause
|
|
206
|
+
if not self._live:
|
|
207
|
+
try:
|
|
208
|
+
# Clear any leftover artifacts before starting
|
|
209
|
+
import sys
|
|
210
|
+
|
|
211
|
+
sys.stdout.write("\r") # Return to start of line
|
|
212
|
+
sys.stdout.write("\x1b[K") # Clear to end of line
|
|
213
|
+
sys.stdout.flush()
|
|
214
|
+
|
|
215
|
+
# Print blank line before spinner for visual separation
|
|
216
|
+
self.console.print()
|
|
217
|
+
|
|
218
|
+
self._live = Live(
|
|
219
|
+
self._generate_spinner_panel(),
|
|
220
|
+
console=self.console,
|
|
221
|
+
refresh_per_second=8,
|
|
222
|
+
transient=True, # Clear spinner line when stopped
|
|
223
|
+
auto_refresh=False,
|
|
224
|
+
)
|
|
225
|
+
self._live.start()
|
|
226
|
+
except Exception:
|
|
227
|
+
pass
|
|
228
|
+
else:
|
|
229
|
+
# If live display still exists, clear console state first
|
|
230
|
+
try:
|
|
231
|
+
# Force Rich to reset any cached console state
|
|
232
|
+
if hasattr(self.console, "_buffer"):
|
|
233
|
+
# Clear Rich's internal buffer to prevent artifacts
|
|
234
|
+
self.console.file.write("\r") # Return to start
|
|
235
|
+
self.console.file.write("\x1b[K") # Clear line
|
|
236
|
+
self.console.file.flush()
|
|
237
|
+
|
|
238
|
+
self._live.update(self._generate_spinner_panel())
|
|
239
|
+
self._live.refresh()
|
|
240
|
+
except Exception:
|
|
241
|
+
pass
|
|
242
|
+
|
|
243
|
+
def __enter__(self):
|
|
244
|
+
"""Support for context manager."""
|
|
245
|
+
self.start()
|
|
246
|
+
return self
|
|
247
|
+
|
|
248
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
249
|
+
"""Clean up when exiting context manager."""
|
|
250
|
+
self.stop()
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base spinner implementation to be extended for different UI modes.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from abc import ABC, abstractmethod
|
|
6
|
+
from threading import Lock
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class SpinnerBase(ABC):
|
|
10
|
+
"""Abstract base class for spinner implementations."""
|
|
11
|
+
|
|
12
|
+
# Shared spinner frames across implementations
|
|
13
|
+
FRAMES = ["◝", "◜", "◠", "◡"]
|
|
14
|
+
|
|
15
|
+
# Default message when processing
|
|
16
|
+
THINKING_MESSAGE = "Thinking... "
|
|
17
|
+
|
|
18
|
+
# Message when waiting for user input
|
|
19
|
+
WAITING_MESSAGE = "Waiting... "
|
|
20
|
+
|
|
21
|
+
# Current message - starts with thinking by default
|
|
22
|
+
MESSAGE = THINKING_MESSAGE
|
|
23
|
+
|
|
24
|
+
_context_info: str = ""
|
|
25
|
+
_context_lock: Lock = Lock()
|
|
26
|
+
|
|
27
|
+
def __init__(self):
|
|
28
|
+
"""Initialize the spinner."""
|
|
29
|
+
self._is_spinning = False
|
|
30
|
+
self._frame_index = 0
|
|
31
|
+
|
|
32
|
+
@abstractmethod
|
|
33
|
+
def start(self):
|
|
34
|
+
"""Start the spinner animation."""
|
|
35
|
+
self._is_spinning = True
|
|
36
|
+
self._frame_index = 0
|
|
37
|
+
|
|
38
|
+
@abstractmethod
|
|
39
|
+
def stop(self):
|
|
40
|
+
"""Stop the spinner animation."""
|
|
41
|
+
self._is_spinning = False
|
|
42
|
+
|
|
43
|
+
@abstractmethod
|
|
44
|
+
def update_frame(self):
|
|
45
|
+
"""Update to the next frame."""
|
|
46
|
+
if self._is_spinning:
|
|
47
|
+
self._frame_index = (self._frame_index + 1) % len(self.FRAMES)
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def current_frame(self):
|
|
51
|
+
"""Get the current frame."""
|
|
52
|
+
return self.FRAMES[self._frame_index]
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def is_spinning(self):
|
|
56
|
+
"""Check if the spinner is currently spinning."""
|
|
57
|
+
return self._is_spinning
|
|
58
|
+
|
|
59
|
+
@classmethod
|
|
60
|
+
def set_context_info(cls, info: str) -> None:
|
|
61
|
+
"""Set shared context information displayed beside the spinner."""
|
|
62
|
+
with cls._context_lock:
|
|
63
|
+
cls._context_info = info
|
|
64
|
+
|
|
65
|
+
@classmethod
|
|
66
|
+
def clear_context_info(cls) -> None:
|
|
67
|
+
"""Clear any context information displayed beside the spinner."""
|
|
68
|
+
cls.set_context_info("")
|
|
69
|
+
|
|
70
|
+
@classmethod
|
|
71
|
+
def get_context_info(cls) -> str:
|
|
72
|
+
"""Return the current spinner context information."""
|
|
73
|
+
with cls._context_lock:
|
|
74
|
+
return cls._context_info
|
|
75
|
+
|
|
76
|
+
@staticmethod
|
|
77
|
+
def format_context_info(total_tokens: int, capacity: int, proportion: float) -> str:
|
|
78
|
+
"""Create a concise context summary for spinner display."""
|
|
79
|
+
if capacity <= 0:
|
|
80
|
+
return ""
|
|
81
|
+
proportion_pct = proportion * 100
|
|
82
|
+
return f"Tokens: {total_tokens:,}/{capacity:,} ({proportion_pct:.1f}% used)"
|