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,188 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Configuration loader for Claude Code hooks.
|
|
3
|
+
|
|
4
|
+
Loads and merges hooks from multiple locations:
|
|
5
|
+
1. ~/.muse/hooks.json (global level) - always loaded if exists
|
|
6
|
+
2. .claude/settings.json (project-level) - merged with global
|
|
7
|
+
|
|
8
|
+
Both configurations are loaded and merged so that hooks from both levels
|
|
9
|
+
coexist and execute together.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
import logging
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Any
|
|
16
|
+
|
|
17
|
+
from code_muse.hook_engine.trust import compute_content_hash, is_hook_trusted
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
PROJECT_HOOKS_FILE = ".claude/settings.json"
|
|
22
|
+
GLOBAL_HOOKS_FILE = Path("~/.muse/hooks.json").expanduser()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _deep_merge_hooks(base: dict[str, Any], overlay: dict[str, Any]) -> dict[str, Any]:
|
|
26
|
+
"""
|
|
27
|
+
Merge hook configurations, combining event types and hook groups.
|
|
28
|
+
|
|
29
|
+
When the same event type exists in both base and overlay, their hook groups
|
|
30
|
+
are concatenated (overlay hooks appear after base hooks).
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
base: Base configuration dictionary
|
|
34
|
+
overlay: Configuration to merge on top
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
Merged configuration with all hooks from both sources
|
|
38
|
+
"""
|
|
39
|
+
merged = dict(base)
|
|
40
|
+
|
|
41
|
+
for event_type, hook_groups in overlay.items():
|
|
42
|
+
if event_type.startswith("_"):
|
|
43
|
+
# Skip comment keys
|
|
44
|
+
merged[event_type] = hook_groups
|
|
45
|
+
continue
|
|
46
|
+
|
|
47
|
+
if event_type not in merged:
|
|
48
|
+
# New event type, just add it
|
|
49
|
+
merged[event_type] = hook_groups
|
|
50
|
+
elif isinstance(merged[event_type], list) and isinstance(hook_groups, list):
|
|
51
|
+
# Both are lists, concatenate them (overlay hooks come after)
|
|
52
|
+
merged[event_type] = merged[event_type] + hook_groups
|
|
53
|
+
logger.debug(
|
|
54
|
+
f"Merged {len(hook_groups)} hook group(s) for event '{event_type}'"
|
|
55
|
+
)
|
|
56
|
+
else:
|
|
57
|
+
# Type mismatch or unexpected structure, keep base
|
|
58
|
+
logger.warning(
|
|
59
|
+
f"Cannot merge event type '{event_type}': type mismatch or unexpected structure"
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
return merged
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def load_hooks_config() -> dict[str, Any | None]:
|
|
66
|
+
"""
|
|
67
|
+
Load and merge hooks configuration from available sources.
|
|
68
|
+
|
|
69
|
+
Priority order:
|
|
70
|
+
1. ~/.muse/hooks.json (global level) - always loaded if exists
|
|
71
|
+
2. .claude/settings.json (project-level) - merged with global
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
Merged configuration dictionary or None if no config found.
|
|
75
|
+
This function preserves backward compatibility; trust metadata
|
|
76
|
+
is handled separately via load_hooks_config_with_sources().
|
|
77
|
+
"""
|
|
78
|
+
config, _sources = load_hooks_config_with_sources()
|
|
79
|
+
return config
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def load_hooks_config_with_sources() -> tuple[dict[str, Any | None], list[dict]]:
|
|
83
|
+
"""
|
|
84
|
+
Load and merge hooks configuration from available sources with trust metadata.
|
|
85
|
+
|
|
86
|
+
Priority order:
|
|
87
|
+
1. ~/.muse/hooks.json (global level) - always loaded if exists
|
|
88
|
+
2. .claude/settings.json (project-level) - merged with global
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
Tuple of (merged configuration dictionary, list of source metadata dicts).
|
|
92
|
+
Each source metadata dict contains:
|
|
93
|
+
- path: file path
|
|
94
|
+
- source: "global" | "project"
|
|
95
|
+
- config: the raw hooks dict from this source
|
|
96
|
+
- content_hash: SHA-256 of file content
|
|
97
|
+
- trusted: bool
|
|
98
|
+
"""
|
|
99
|
+
merged_config: dict[str, Any] = {}
|
|
100
|
+
sources: list[dict] = []
|
|
101
|
+
|
|
102
|
+
# Load global hooks first
|
|
103
|
+
global_config_path = Path(GLOBAL_HOOKS_FILE)
|
|
104
|
+
|
|
105
|
+
if global_config_path.exists():
|
|
106
|
+
try:
|
|
107
|
+
raw = global_config_path.read_text(encoding="utf-8")
|
|
108
|
+
config = json.loads(raw)
|
|
109
|
+
hooks_part = None
|
|
110
|
+
if "hooks" in config and isinstance(config["hooks"], dict):
|
|
111
|
+
logger.info(
|
|
112
|
+
f"Loaded hooks configuration (wrapped format) from {GLOBAL_HOOKS_FILE}"
|
|
113
|
+
)
|
|
114
|
+
hooks_part = config["hooks"]
|
|
115
|
+
elif isinstance(config, dict):
|
|
116
|
+
logger.info(f"Loaded hooks configuration from {GLOBAL_HOOKS_FILE}")
|
|
117
|
+
hooks_part = config
|
|
118
|
+
if hooks_part is not None:
|
|
119
|
+
merged_config = _deep_merge_hooks(merged_config, hooks_part)
|
|
120
|
+
sources.append(
|
|
121
|
+
{
|
|
122
|
+
"path": str(global_config_path),
|
|
123
|
+
"source": "global",
|
|
124
|
+
"config": hooks_part,
|
|
125
|
+
"content_hash": compute_content_hash(raw),
|
|
126
|
+
"trusted": True,
|
|
127
|
+
}
|
|
128
|
+
)
|
|
129
|
+
except json.JSONDecodeError as e:
|
|
130
|
+
logger.error(f"Invalid JSON in {GLOBAL_HOOKS_FILE}: {e}")
|
|
131
|
+
except Exception as e:
|
|
132
|
+
logger.error(f"Failed to load {GLOBAL_HOOKS_FILE}: {e}", exc_info=True)
|
|
133
|
+
|
|
134
|
+
# Load and merge project-level hooks
|
|
135
|
+
project_config_path = Path.cwd() / PROJECT_HOOKS_FILE
|
|
136
|
+
|
|
137
|
+
if project_config_path.exists():
|
|
138
|
+
try:
|
|
139
|
+
raw = project_config_path.read_text(encoding="utf-8")
|
|
140
|
+
config = json.loads(raw)
|
|
141
|
+
hooks_config = config.get("hooks")
|
|
142
|
+
if hooks_config:
|
|
143
|
+
logger.info(f"Merging hooks configuration from {project_config_path}")
|
|
144
|
+
merged_config = _deep_merge_hooks(merged_config, hooks_config)
|
|
145
|
+
content_hash = compute_content_hash(raw)
|
|
146
|
+
project_root = Path.cwd().resolve()
|
|
147
|
+
trusted = is_hook_trusted(
|
|
148
|
+
project_root,
|
|
149
|
+
project_config_path.resolve(),
|
|
150
|
+
content_hash,
|
|
151
|
+
)
|
|
152
|
+
sources.append(
|
|
153
|
+
{
|
|
154
|
+
"path": str(project_config_path),
|
|
155
|
+
"source": "project",
|
|
156
|
+
"config": hooks_config,
|
|
157
|
+
"content_hash": content_hash,
|
|
158
|
+
"trusted": trusted,
|
|
159
|
+
}
|
|
160
|
+
)
|
|
161
|
+
else:
|
|
162
|
+
logger.debug(f"No 'hooks' section found in {project_config_path}")
|
|
163
|
+
except json.JSONDecodeError as e:
|
|
164
|
+
logger.error(f"Invalid JSON in {project_config_path}: {e}")
|
|
165
|
+
except Exception as e:
|
|
166
|
+
logger.error(f"Failed to load {project_config_path}: {e}", exc_info=True)
|
|
167
|
+
|
|
168
|
+
if not merged_config:
|
|
169
|
+
logger.debug("No hooks configuration found")
|
|
170
|
+
return None, sources
|
|
171
|
+
|
|
172
|
+
event_count = len([event for event in merged_config if not event.startswith("_")])
|
|
173
|
+
logger.info(f"Hooks configuration ready ({event_count} event type(s))")
|
|
174
|
+
return merged_config, sources
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def get_hooks_config_paths() -> list[Path]:
|
|
178
|
+
"""
|
|
179
|
+
Return list of hook configuration paths.
|
|
180
|
+
|
|
181
|
+
Returns paths in order of precedence (project-level first, then global).
|
|
182
|
+
Note: internally, hooks are loaded in reverse order (global first, then project)
|
|
183
|
+
so that project-level hooks can extend/append to global hooks.
|
|
184
|
+
"""
|
|
185
|
+
return [
|
|
186
|
+
Path.cwd() / PROJECT_HOOKS_FILE,
|
|
187
|
+
GLOBAL_HOOKS_FILE,
|
|
188
|
+
]
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Register callbacks for Claude Code hooks plugin.
|
|
3
|
+
|
|
4
|
+
Integrates the hook engine with code_muse's callback system.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from code_muse.callbacks import register_callback
|
|
11
|
+
from code_muse.hook_engine import EventData, HookEngine
|
|
12
|
+
|
|
13
|
+
from .config import load_hooks_config_with_sources
|
|
14
|
+
|
|
15
|
+
_SUBAGENT_NAMES = frozenset(
|
|
16
|
+
{
|
|
17
|
+
"pack_leader",
|
|
18
|
+
"bloodhound",
|
|
19
|
+
"muse",
|
|
20
|
+
"code_muse",
|
|
21
|
+
"retriever",
|
|
22
|
+
"shepherd",
|
|
23
|
+
"terrier",
|
|
24
|
+
"watchdog",
|
|
25
|
+
"subagent",
|
|
26
|
+
"sub_agent",
|
|
27
|
+
}
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
logger = logging.getLogger(__name__)
|
|
31
|
+
|
|
32
|
+
_hook_engine: HookEngine | None = None
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _initialize_engine() -> HookEngine | None:
|
|
36
|
+
from code_muse.hook_engine.models import HookRegistry
|
|
37
|
+
from code_muse.hook_engine.registry import build_registry_from_config
|
|
38
|
+
|
|
39
|
+
merged_config, sources = load_hooks_config_with_sources()
|
|
40
|
+
|
|
41
|
+
if not merged_config:
|
|
42
|
+
logger.info("No hooks configuration found - Claude Code hooks disabled")
|
|
43
|
+
return None
|
|
44
|
+
|
|
45
|
+
try:
|
|
46
|
+
# Build registry per-source so project hooks can be marked untrusted
|
|
47
|
+
registry = HookRegistry()
|
|
48
|
+
for source_info in sources:
|
|
49
|
+
source = source_info.get("source", "global")
|
|
50
|
+
trusted = source_info.get("trusted", source == "global")
|
|
51
|
+
cfg = source_info.get("config", {})
|
|
52
|
+
if not cfg:
|
|
53
|
+
continue
|
|
54
|
+
sub_registry = build_registry_from_config(
|
|
55
|
+
cfg,
|
|
56
|
+
source=source, # type: ignore[arg-type]
|
|
57
|
+
trusted=trusted,
|
|
58
|
+
)
|
|
59
|
+
# Merge sub_registry into main registry
|
|
60
|
+
for attr_name in [
|
|
61
|
+
"pre_tool_use",
|
|
62
|
+
"post_tool_use",
|
|
63
|
+
"session_start",
|
|
64
|
+
"session_end",
|
|
65
|
+
"pre_compact",
|
|
66
|
+
"user_prompt_submit",
|
|
67
|
+
"notification",
|
|
68
|
+
"stop",
|
|
69
|
+
"subagent_stop",
|
|
70
|
+
]:
|
|
71
|
+
for hook in getattr(sub_registry, attr_name, []):
|
|
72
|
+
registry.add_hook(attr_name, hook)
|
|
73
|
+
|
|
74
|
+
engine = HookEngine(strict_validation=False)
|
|
75
|
+
engine._registry = registry
|
|
76
|
+
stats = engine.get_stats()
|
|
77
|
+
logger.info(
|
|
78
|
+
f"Hook engine ready - Total: {stats['total_hooks']}, "
|
|
79
|
+
f"Enabled: {stats['enabled_hooks']}"
|
|
80
|
+
)
|
|
81
|
+
return engine
|
|
82
|
+
except Exception as e:
|
|
83
|
+
logger.error(f"Failed to initialize hook engine: {e}", exc_info=True)
|
|
84
|
+
return None
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
_hook_engine = _initialize_engine()
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
async def on_pre_tool_call_hook(
|
|
91
|
+
tool_name: str,
|
|
92
|
+
tool_args: dict[str, Any],
|
|
93
|
+
context: Any = None,
|
|
94
|
+
) -> dict[str, Any | None]:
|
|
95
|
+
"""Pre-tool callback — executes hooks before tool runs. Can block."""
|
|
96
|
+
if not _hook_engine:
|
|
97
|
+
return None
|
|
98
|
+
|
|
99
|
+
event_data = EventData(
|
|
100
|
+
event_type="PreToolUse",
|
|
101
|
+
tool_name=tool_name,
|
|
102
|
+
tool_args=tool_args,
|
|
103
|
+
context={"context": context} if context else {},
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
try:
|
|
107
|
+
result = await _hook_engine.process_event("PreToolUse", event_data)
|
|
108
|
+
|
|
109
|
+
if result.blocked:
|
|
110
|
+
logger.debug(
|
|
111
|
+
f"Tool '{tool_name}' blocked by hook: {result.blocking_reason}"
|
|
112
|
+
)
|
|
113
|
+
return {
|
|
114
|
+
"blocked": True,
|
|
115
|
+
"reason": result.blocking_reason,
|
|
116
|
+
"error_message": result.blocking_reason,
|
|
117
|
+
}
|
|
118
|
+
return None
|
|
119
|
+
except Exception as e:
|
|
120
|
+
logger.error(f"Error in pre-tool hook: {e}", exc_info=True)
|
|
121
|
+
return None
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
async def on_post_tool_call_hook(
|
|
125
|
+
tool_name: str,
|
|
126
|
+
tool_args: dict[str, Any],
|
|
127
|
+
result: Any,
|
|
128
|
+
duration_ms: float,
|
|
129
|
+
context: Any = None,
|
|
130
|
+
) -> None:
|
|
131
|
+
"""Post-tool callback — executes hooks after tool completes (observation only)."""
|
|
132
|
+
if not _hook_engine:
|
|
133
|
+
return
|
|
134
|
+
|
|
135
|
+
event_data = EventData(
|
|
136
|
+
event_type="PostToolUse",
|
|
137
|
+
tool_name=tool_name,
|
|
138
|
+
tool_args=tool_args,
|
|
139
|
+
context={"result": result, "duration_ms": duration_ms, "context": context},
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
try:
|
|
143
|
+
await _hook_engine.process_event("PostToolUse", event_data)
|
|
144
|
+
except Exception as e:
|
|
145
|
+
logger.error(f"Error in post-tool hook: {e}", exc_info=True)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
register_callback("pre_tool_call", on_pre_tool_call_hook)
|
|
149
|
+
register_callback("post_tool_call", on_post_tool_call_hook)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
async def on_startup_hook() -> None:
|
|
153
|
+
"""Startup callback — fires SessionStart hooks when code_muse boots."""
|
|
154
|
+
if not _hook_engine:
|
|
155
|
+
return
|
|
156
|
+
|
|
157
|
+
event_data = EventData(
|
|
158
|
+
event_type="SessionStart",
|
|
159
|
+
tool_name="session",
|
|
160
|
+
tool_args={},
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
try:
|
|
164
|
+
await _hook_engine.process_event("SessionStart", event_data)
|
|
165
|
+
except Exception as e:
|
|
166
|
+
logger.error(f"Error in SessionStart hook: {e}", exc_info=True)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
async def on_agent_run_end_hook(
|
|
170
|
+
agent_name: str,
|
|
171
|
+
model_name: str,
|
|
172
|
+
session_id: str | None = None,
|
|
173
|
+
success: bool = True,
|
|
174
|
+
error: Exception | None = None,
|
|
175
|
+
response_text: str | None = None,
|
|
176
|
+
metadata: dict | None = None,
|
|
177
|
+
) -> None:
|
|
178
|
+
"""agent_run_end callback — fires Stop or SubagentStop hooks."""
|
|
179
|
+
if not _hook_engine:
|
|
180
|
+
return
|
|
181
|
+
|
|
182
|
+
agent_lower = (agent_name or "").lower()
|
|
183
|
+
is_subagent = any(name in agent_lower for name in _SUBAGENT_NAMES)
|
|
184
|
+
event_type = "SubagentStop" if is_subagent else "Stop"
|
|
185
|
+
|
|
186
|
+
event_data = EventData(
|
|
187
|
+
event_type=event_type,
|
|
188
|
+
tool_name=agent_name or "agent",
|
|
189
|
+
tool_args={},
|
|
190
|
+
context={
|
|
191
|
+
"agent_name": agent_name,
|
|
192
|
+
"model_name": model_name,
|
|
193
|
+
"session_id": session_id,
|
|
194
|
+
"success": success,
|
|
195
|
+
"error": str(error) if error else None,
|
|
196
|
+
},
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
try:
|
|
200
|
+
await _hook_engine.process_event(event_type, event_data)
|
|
201
|
+
except Exception as e:
|
|
202
|
+
logger.error(f"Error in {event_type} hook: {e}", exc_info=True)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
register_callback("startup", on_startup_hook)
|
|
206
|
+
register_callback("agent_run_end", on_agent_run_end_hook)
|
|
207
|
+
|
|
208
|
+
logger.info("Claude Code hooks plugin registered")
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# Claude Code OAuth Plugin
|
|
2
|
+
|
|
3
|
+
This plugin adds OAuth authentication for Claude Code to Muse, automatically importing available models into your configuration.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **OAuth Authentication**: Secure OAuth flow for Claude Code using PKCE
|
|
8
|
+
- **Automatic Model Discovery**: Fetches available models from the Claude API once authenticated
|
|
9
|
+
- **Model Registration**: Automatically adds models to `extra_models.json` with the `claude-code-` prefix
|
|
10
|
+
- **Token Management**: Secure storage of OAuth tokens in the Muse config directory
|
|
11
|
+
- **Browser Integration**: Launches the Claude OAuth consent flow automatically
|
|
12
|
+
- **Callback Capture**: Listens on localhost to receive and process the OAuth redirect
|
|
13
|
+
|
|
14
|
+
## Commands
|
|
15
|
+
|
|
16
|
+
### `/claude-code-auth`
|
|
17
|
+
Authenticate with Claude Code via OAuth and import available models.
|
|
18
|
+
|
|
19
|
+
This will:
|
|
20
|
+
1. Launch the Claude OAuth consent flow in your browser
|
|
21
|
+
2. Walk you through approving access for the shared `claude-cli` client
|
|
22
|
+
3. Capture the redirect from Claude in a temporary local callback server
|
|
23
|
+
4. Exchange the returned code for access tokens and store them securely
|
|
24
|
+
5. Fetch available models from Claude Code and add them to your configuration
|
|
25
|
+
|
|
26
|
+
### `/claude-code-status`
|
|
27
|
+
Check Claude Code OAuth authentication status and configured models.
|
|
28
|
+
|
|
29
|
+
Shows:
|
|
30
|
+
- Current authentication status
|
|
31
|
+
- Token expiry information (if available)
|
|
32
|
+
- Number and names of configured Claude Code models
|
|
33
|
+
|
|
34
|
+
### `/claude-code-logout`
|
|
35
|
+
Remove Claude Code OAuth tokens and imported models.
|
|
36
|
+
|
|
37
|
+
This will:
|
|
38
|
+
1. Remove stored OAuth tokens
|
|
39
|
+
2. Remove all Claude Code models from `extra_models.json`
|
|
40
|
+
|
|
41
|
+
## Setup
|
|
42
|
+
|
|
43
|
+
### Prerequisites
|
|
44
|
+
|
|
45
|
+
1. **Claude account** with access to the Claude Console developer settings
|
|
46
|
+
2. **Browser access** to generate authorization codes
|
|
47
|
+
|
|
48
|
+
### Configuration
|
|
49
|
+
|
|
50
|
+
The plugin ships with sensible defaults in `config.py`:
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
CLAUDE_CODE_OAUTH_CONFIG = {
|
|
54
|
+
"auth_url": "https://claude.ai/oauth/authorize",
|
|
55
|
+
"token_url": "https://console.anthropic.com/v1/oauth/token",
|
|
56
|
+
"api_base_url": "https://api.anthropic.com",
|
|
57
|
+
"client_id": "9d1c250a-e61b-44d9-88ed-5944d1962f5e",
|
|
58
|
+
"scope": "org:create_api_key user:profile user:inference",
|
|
59
|
+
"redirect_host": "http://localhost",
|
|
60
|
+
"redirect_path": "callback",
|
|
61
|
+
"callback_port_range": (8765, 8795),
|
|
62
|
+
"callback_timeout": 180,
|
|
63
|
+
"prefix": "claude-code-",
|
|
64
|
+
"default_context_length": 200000,
|
|
65
|
+
"api_key_env_var": "CLAUDE_CODE_ACCESS_TOKEN",
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
These values mirror the public client used by llxprt-code. Adjust only if Anthropic changes their configuration.
|
|
70
|
+
|
|
71
|
+
### Environment Variables
|
|
72
|
+
|
|
73
|
+
After authentication, the models will reference:
|
|
74
|
+
- `CLAUDE_CODE_ACCESS_TOKEN`: Automatically written by the plugin
|
|
75
|
+
|
|
76
|
+
## Usage Example
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
# Authenticate with Claude Code
|
|
80
|
+
/claude-code-auth
|
|
81
|
+
|
|
82
|
+
# Check status
|
|
83
|
+
/claude-code-status
|
|
84
|
+
|
|
85
|
+
# Use a Claude Code model
|
|
86
|
+
/set model claude-code-claude-3-5-sonnet-20241022
|
|
87
|
+
|
|
88
|
+
# When done, logout
|
|
89
|
+
/claude-code-logout
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Model Configuration
|
|
93
|
+
|
|
94
|
+
After authentication, models will be added to `~/.muse/extra_models.json`:
|
|
95
|
+
|
|
96
|
+
```json
|
|
97
|
+
{
|
|
98
|
+
"claude-code-claude-3-5-sonnet-20241022": {
|
|
99
|
+
"type": "anthropic",
|
|
100
|
+
"name": "claude-3-5-sonnet-20241022",
|
|
101
|
+
"custom_endpoint": {
|
|
102
|
+
"url": "https://api.anthropic.com",
|
|
103
|
+
"api_key": "$CLAUDE_CODE_ACCESS_TOKEN"
|
|
104
|
+
},
|
|
105
|
+
"context_length": 200000,
|
|
106
|
+
"oauth_source": "claude-code-plugin"
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Security
|
|
112
|
+
|
|
113
|
+
- **Token Storage**: Tokens are saved to `~/.muse/claude_code_oauth.json` with `0o600` permissions
|
|
114
|
+
- **PKCE Support**: Uses Proof Key for Code Exchange for enhanced security
|
|
115
|
+
- **State Validation**: Checks the returned state (if provided) to guard against CSRF
|
|
116
|
+
- **HTTPS Only**: All OAuth communications use HTTPS endpoints
|
|
117
|
+
|
|
118
|
+
## Troubleshooting
|
|
119
|
+
|
|
120
|
+
### Browser doesn't open
|
|
121
|
+
- Manually visit the URL shown in the output
|
|
122
|
+
- Check that a default browser is configured
|
|
123
|
+
|
|
124
|
+
### Authentication fails
|
|
125
|
+
- Ensure the browser completed the redirect back to Muse (no pop-up blockers)
|
|
126
|
+
- Retry if the window shows an error; codes expire quickly
|
|
127
|
+
- Confirm network access to `claude.ai`
|
|
128
|
+
|
|
129
|
+
### Models not showing up
|
|
130
|
+
- Claude may not return the model list for your account; verify access manually
|
|
131
|
+
- Check `/claude-code-status` to confirm authentication succeeded
|
|
132
|
+
|
|
133
|
+
## Development
|
|
134
|
+
|
|
135
|
+
### File Structure
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
claude_code_oauth/
|
|
139
|
+
├── __init__.py
|
|
140
|
+
├── register_callbacks.py # Main plugin logic and command handlers
|
|
141
|
+
├── config.py # Configuration settings
|
|
142
|
+
├── utils.py # OAuth helpers and file operations
|
|
143
|
+
├── README.md # This file
|
|
144
|
+
├── SETUP.md # Quick setup guide
|
|
145
|
+
└── test_plugin.py # Manual test helper
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Key Components
|
|
149
|
+
|
|
150
|
+
- **OAuth Flow**: Authorization code flow with PKCE and automatic callback capture
|
|
151
|
+
- **Token Management**: Secure storage and retrieval helpers
|
|
152
|
+
- **Model Discovery**: API integration for model fetching
|
|
153
|
+
- **Plugin Registration**: Custom command handlers wired into Muse
|
|
154
|
+
|
|
155
|
+
## Notes
|
|
156
|
+
|
|
157
|
+
- The plugin assumes Anthropic continues to expose the shared `claude-cli` OAuth client
|
|
158
|
+
- Tokens are refreshed on subsequent API calls if the service returns refresh tokens
|
|
159
|
+
- Models are prefixed with `claude-code-` to avoid collisions with other Anthropic models
|
|
160
|
+
|
|
161
|
+
## Contributing
|
|
162
|
+
|
|
163
|
+
When modifying this plugin:
|
|
164
|
+
1. Maintain security best practices
|
|
165
|
+
2. Test OAuth flow changes manually before shipping
|
|
166
|
+
3. Update documentation for any configuration or UX changes
|
|
167
|
+
4. Keep files under 600 lines; split into helpers when needed
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Claude Code OAuth Plugin Setup Guide
|
|
2
|
+
|
|
3
|
+
This guide walks you through using the Claude Code OAuth plugin inside Muse.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
1. Ensure the plugin files live under `code_muse/plugins/claude_code_oauth/`
|
|
8
|
+
2. Restart Muse so it loads the plugin
|
|
9
|
+
3. Run `/claude-code-auth` and follow the prompts
|
|
10
|
+
|
|
11
|
+
## Why No Client Registration?
|
|
12
|
+
|
|
13
|
+
Anthropic exposes a shared **public client** (`claude-cli`) for command-line tools. That means:
|
|
14
|
+
- No client secret is needed
|
|
15
|
+
- Everyone authenticates through Claude Console
|
|
16
|
+
- Security is enforced with PKCE and per-user tokens
|
|
17
|
+
|
|
18
|
+
## Authentication Flow
|
|
19
|
+
|
|
20
|
+
1. Call `/claude-code-auth`
|
|
21
|
+
2. Your browser opens the Claude OAuth consent flow at `https://claude.ai/oauth/authorize`
|
|
22
|
+
3. Sign in (or pick an account) and approve the "Claude CLI" access request
|
|
23
|
+
4. The browser closes automatically after the redirect is captured
|
|
24
|
+
5. Tokens are stored locally at `~/.muse/claude_code_oauth.json`
|
|
25
|
+
6. Available Claude Code models are fetched and added to `extra_models.json`
|
|
26
|
+
|
|
27
|
+
## Commands Recap
|
|
28
|
+
|
|
29
|
+
- `/claude-code-auth` – Authenticate and sync models
|
|
30
|
+
- `/claude-code-status` – Show auth status, expiry, configured models
|
|
31
|
+
- `/claude-code-logout` – Remove tokens and any models added by the plugin
|
|
32
|
+
|
|
33
|
+
## Configuration Defaults
|
|
34
|
+
|
|
35
|
+
`config.py` ships with values aligned to llxprt-code:
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
CLAUDE_CODE_OAUTH_CONFIG = {
|
|
39
|
+
"auth_url": "https://claude.ai/oauth/authorize",
|
|
40
|
+
"token_url": "https://console.anthropic.com/v1/oauth/token",
|
|
41
|
+
"api_base_url": "https://api.anthropic.com",
|
|
42
|
+
"client_id": "9d1c250a-e61b-44d9-88ed-5944d1962f5e",
|
|
43
|
+
"scope": "org:create_api_key user:profile user:inference",
|
|
44
|
+
"redirect_host": "http://localhost",
|
|
45
|
+
"redirect_path": "callback",
|
|
46
|
+
"callback_port_range": (8765, 8795),
|
|
47
|
+
"callback_timeout": 180,
|
|
48
|
+
"prefix": "claude-code-",
|
|
49
|
+
"default_context_length": 200000,
|
|
50
|
+
"api_key_env_var": "CLAUDE_CODE_ACCESS_TOKEN",
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Change these only if Anthropic updates their endpoints or scopes.
|
|
55
|
+
|
|
56
|
+
## After Authentication
|
|
57
|
+
|
|
58
|
+
- Models appear in `~/.muse/extra_models.json` with the `claude-code-` prefix
|
|
59
|
+
- The environment variable `CLAUDE_CODE_ACCESS_TOKEN` is used by those models
|
|
60
|
+
- `/claude-code-status` shows token expiry when the API provides it
|
|
61
|
+
|
|
62
|
+
## Troubleshooting Tips
|
|
63
|
+
|
|
64
|
+
- **Browser did not open** – Copy the displayed URL into your browser manually
|
|
65
|
+
- **Invalid code** – The code expires quickly; generate a new one in Claude Console
|
|
66
|
+
- **State mismatch** – Rare, but rerun `/claude-code-auth` if the browser reports a mismatch
|
|
67
|
+
- **No models added** – Your account might lack Claude Code access; tokens are still stored for later use
|
|
68
|
+
|
|
69
|
+
## Files Created
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
~/.muse/
|
|
73
|
+
├── claude_code_oauth.json # OAuth tokens (0600 permissions)
|
|
74
|
+
└── extra_models.json # Extended model registry
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Manual Testing
|
|
78
|
+
|
|
79
|
+
Run the helper script for sanity checks:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
python code_muse/plugins/claude_code_oauth/test_plugin.py
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
It verifies imports, configuration values, and filesystem expectations without hitting the Anthropic API.
|
|
86
|
+
|
|
87
|
+
## Security Notes
|
|
88
|
+
|
|
89
|
+
- Tokens are stored locally and never transmitted elsewhere
|
|
90
|
+
- PKCE protects the flow even without a client secret
|
|
91
|
+
- HTTPS endpoints are enforced for all requests
|
|
92
|
+
|
|
93
|
+
Enjoy hacking with Claude Code straight from Muse.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Claude Code OAuth Plugin for Muse
|
|
3
|
+
|
|
4
|
+
This plugin provides OAuth authentication for Claude Code and automatically
|
|
5
|
+
adds available models to the extra_models.json configuration.
|
|
6
|
+
|
|
7
|
+
The plugin also includes a token refresh heartbeat for maintaining fresh
|
|
8
|
+
tokens during long-running agentic operations.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from .token_refresh_heartbeat import (
|
|
12
|
+
TokenRefreshHeartbeat,
|
|
13
|
+
force_token_refresh,
|
|
14
|
+
get_current_heartbeat,
|
|
15
|
+
is_heartbeat_running,
|
|
16
|
+
token_refresh_heartbeat_context,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"TokenRefreshHeartbeat",
|
|
21
|
+
"token_refresh_heartbeat_context",
|
|
22
|
+
"is_heartbeat_running",
|
|
23
|
+
"get_current_heartbeat",
|
|
24
|
+
"force_token_refresh",
|
|
25
|
+
]
|