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.
Files changed (394) hide show
  1. code_muse/__init__.py +26 -0
  2. code_muse/__main__.py +10 -0
  3. code_muse/agents/__init__.py +31 -0
  4. code_muse/agents/_builder.py +214 -0
  5. code_muse/agents/_compaction.py +506 -0
  6. code_muse/agents/_diagnostics.py +171 -0
  7. code_muse/agents/_history.py +382 -0
  8. code_muse/agents/_key_listeners.py +148 -0
  9. code_muse/agents/_non_streaming_render.py +148 -0
  10. code_muse/agents/_runtime.py +596 -0
  11. code_muse/agents/agent_creator_agent.py +603 -0
  12. code_muse/agents/agent_helios.py +47 -0
  13. code_muse/agents/agent_manager.py +740 -0
  14. code_muse/agents/agent_muse.py +78 -0
  15. code_muse/agents/agent_planning.py +44 -0
  16. code_muse/agents/agent_qa_melpomene.py +207 -0
  17. code_muse/agents/base_agent.py +194 -0
  18. code_muse/agents/event_stream_handler.py +361 -0
  19. code_muse/agents/json_agent.py +201 -0
  20. code_muse/agents/prompt_v3.py +521 -0
  21. code_muse/agents/subagent_stream_handler.py +273 -0
  22. code_muse/callbacks.py +941 -0
  23. code_muse/chatgpt_codex_client.py +333 -0
  24. code_muse/claude_cache_client.py +853 -0
  25. code_muse/cli_runner/__init__.py +319 -0
  26. code_muse/cli_runner/args.py +63 -0
  27. code_muse/cli_runner/loop.py +510 -0
  28. code_muse/cli_runner/resume.py +72 -0
  29. code_muse/cli_runner/runner.py +161 -0
  30. code_muse/command_line/__init__.py +1 -0
  31. code_muse/command_line/add_model_menu.py +1331 -0
  32. code_muse/command_line/agent_menu.py +674 -0
  33. code_muse/command_line/attachments.py +397 -0
  34. code_muse/command_line/autosave_menu.py +709 -0
  35. code_muse/command_line/clipboard.py +528 -0
  36. code_muse/command_line/colors_menu.py +530 -0
  37. code_muse/command_line/command_handler.py +262 -0
  38. code_muse/command_line/command_registry.py +150 -0
  39. code_muse/command_line/config_commands.py +711 -0
  40. code_muse/command_line/core_commands.py +740 -0
  41. code_muse/command_line/diff_menu.py +865 -0
  42. code_muse/command_line/file_path_completion.py +73 -0
  43. code_muse/command_line/load_context_completion.py +57 -0
  44. code_muse/command_line/model_picker_completion.py +512 -0
  45. code_muse/command_line/model_settings_menu.py +983 -0
  46. code_muse/command_line/onboarding_slides.py +162 -0
  47. code_muse/command_line/onboarding_wizard.py +337 -0
  48. code_muse/command_line/pagination.py +41 -0
  49. code_muse/command_line/pin_command_completion.py +329 -0
  50. code_muse/command_line/prompt_toolkit_completion.py +886 -0
  51. code_muse/command_line/session_commands.py +304 -0
  52. code_muse/command_line/shell_passthrough.py +145 -0
  53. code_muse/command_line/skills_completion.py +158 -0
  54. code_muse/command_line/types.py +18 -0
  55. code_muse/command_line/uc_menu.py +908 -0
  56. code_muse/command_line/utils.py +105 -0
  57. code_muse/command_line/wiggum_state.py +77 -0
  58. code_muse/config.py +1138 -0
  59. code_muse/config_agent.py +168 -0
  60. code_muse/config_appearance.py +241 -0
  61. code_muse/config_model.py +357 -0
  62. code_muse/config_security.py +73 -0
  63. code_muse/error_logging.py +132 -0
  64. code_muse/evals/__init__.py +35 -0
  65. code_muse/evals/eval_helpers.py +81 -0
  66. code_muse/evals/eval_runner.py +299 -0
  67. code_muse/evals/sample_evals/__init__.py +1 -0
  68. code_muse/evals/sample_evals/eval_frugal_reads.py +59 -0
  69. code_muse/evals/sample_evals/eval_memory_planning.py +31 -0
  70. code_muse/evals/sample_evals/eval_shell_efficiency.py +39 -0
  71. code_muse/evals/sample_evals/eval_tool_masking.py +33 -0
  72. code_muse/fs_scan_cache/__init__.py +31 -0
  73. code_muse/fs_scan_cache/invalidation_hooks.py +89 -0
  74. code_muse/fs_scan_cache/scan_cache_core.cpython-314-darwin.so +0 -0
  75. code_muse/fs_scan_cache/scan_cache_core.pyx +203 -0
  76. code_muse/fs_scan_cache/tool_integration.py +309 -0
  77. code_muse/fs_scan_cache/ttl_policy.py +44 -0
  78. code_muse/gemini_code_assist.py +383 -0
  79. code_muse/gemini_model.py +838 -0
  80. code_muse/hook_engine/README.md +105 -0
  81. code_muse/hook_engine/__init__.py +21 -0
  82. code_muse/hook_engine/aliases.py +153 -0
  83. code_muse/hook_engine/engine.py +221 -0
  84. code_muse/hook_engine/executor.py +347 -0
  85. code_muse/hook_engine/matcher.py +154 -0
  86. code_muse/hook_engine/models.py +245 -0
  87. code_muse/hook_engine/registry.py +114 -0
  88. code_muse/hook_engine/trust.py +268 -0
  89. code_muse/hook_engine/validator.py +144 -0
  90. code_muse/http_utils.py +360 -0
  91. code_muse/keymap.py +128 -0
  92. code_muse/list_filtering.py +26 -0
  93. code_muse/main.py +10 -0
  94. code_muse/messaging/__init__.py +259 -0
  95. code_muse/messaging/bus.py +621 -0
  96. code_muse/messaging/commands.py +166 -0
  97. code_muse/messaging/markdown_patches.py +57 -0
  98. code_muse/messaging/message_queue.py +397 -0
  99. code_muse/messaging/messages.py +591 -0
  100. code_muse/messaging/queue_console.py +269 -0
  101. code_muse/messaging/renderers.py +308 -0
  102. code_muse/messaging/rich_renderer.py +1158 -0
  103. code_muse/messaging/shimmer.py +154 -0
  104. code_muse/messaging/spinner/__init__.py +87 -0
  105. code_muse/messaging/spinner/console_spinner.py +250 -0
  106. code_muse/messaging/spinner/spinner_base.py +82 -0
  107. code_muse/messaging/subagent_console.py +458 -0
  108. code_muse/model_factory.py +1203 -0
  109. code_muse/model_switching.py +59 -0
  110. code_muse/model_utils.py +156 -0
  111. code_muse/models.json +66 -0
  112. code_muse/models_cache/__init__.py +26 -0
  113. code_muse/models_cache/blocking_lru_cache.py +98 -0
  114. code_muse/models_cache/cache_writer.py +86 -0
  115. code_muse/models_cache/sha256_hash.cpython-314-darwin.so +0 -0
  116. code_muse/models_cache/sha256_hash.pyx +34 -0
  117. code_muse/models_cache/startup_integration.py +75 -0
  118. code_muse/models_dev_api.json +1 -0
  119. code_muse/models_dev_parser.py +590 -0
  120. code_muse/motion.py +126 -0
  121. code_muse/plugins/__init__.py +471 -0
  122. code_muse/plugins/agent_skills/__init__.py +32 -0
  123. code_muse/plugins/agent_skills/config.py +176 -0
  124. code_muse/plugins/agent_skills/discovery.py +309 -0
  125. code_muse/plugins/agent_skills/downloader.py +389 -0
  126. code_muse/plugins/agent_skills/installer.py +19 -0
  127. code_muse/plugins/agent_skills/metadata.py +293 -0
  128. code_muse/plugins/agent_skills/prompt_builder.py +66 -0
  129. code_muse/plugins/agent_skills/register_callbacks.py +298 -0
  130. code_muse/plugins/agent_skills/remote_catalog.py +320 -0
  131. code_muse/plugins/agent_skills/skill_catalog.py +254 -0
  132. code_muse/plugins/agent_skills/skills_install_menu.py +690 -0
  133. code_muse/plugins/agent_skills/skills_menu.py +791 -0
  134. code_muse/plugins/autonomous_memory/__init__.py +39 -0
  135. code_muse/plugins/autonomous_memory/bm25_scorer.cpython-314-darwin.so +0 -0
  136. code_muse/plugins/autonomous_memory/bm25_scorer.cpython-314-x86_64-linux-gnu.so +0 -0
  137. code_muse/plugins/autonomous_memory/bm25_scorer.pyx +291 -0
  138. code_muse/plugins/autonomous_memory/consolidation.py +82 -0
  139. code_muse/plugins/autonomous_memory/extraction.py +382 -0
  140. code_muse/plugins/autonomous_memory/lease_lock.py +105 -0
  141. code_muse/plugins/autonomous_memory/memory_injection.py +59 -0
  142. code_muse/plugins/autonomous_memory/register_callbacks.py +268 -0
  143. code_muse/plugins/autonomous_memory/secret_scanner.py +62 -0
  144. code_muse/plugins/autonomous_memory/session_scanner.py +163 -0
  145. code_muse/plugins/aws_bedrock/__init__.py +14 -0
  146. code_muse/plugins/aws_bedrock/config.py +99 -0
  147. code_muse/plugins/aws_bedrock/register_callbacks.py +241 -0
  148. code_muse/plugins/aws_bedrock/utils.py +153 -0
  149. code_muse/plugins/azure_foundry/README.md +238 -0
  150. code_muse/plugins/azure_foundry/__init__.py +15 -0
  151. code_muse/plugins/azure_foundry/config.py +125 -0
  152. code_muse/plugins/azure_foundry/discovery.py +187 -0
  153. code_muse/plugins/azure_foundry/register_callbacks.py +495 -0
  154. code_muse/plugins/azure_foundry/token.py +180 -0
  155. code_muse/plugins/azure_foundry/utils.py +345 -0
  156. code_muse/plugins/build_filter/__init__.py +1 -0
  157. code_muse/plugins/build_filter/register_callbacks.py +201 -0
  158. code_muse/plugins/build_filter/strategies/__init__.py +1 -0
  159. code_muse/plugins/build_filter/strategies/build.py +397 -0
  160. code_muse/plugins/chatgpt_oauth/__init__.py +6 -0
  161. code_muse/plugins/chatgpt_oauth/config.py +52 -0
  162. code_muse/plugins/chatgpt_oauth/oauth_flow.py +338 -0
  163. code_muse/plugins/chatgpt_oauth/register_callbacks.py +172 -0
  164. code_muse/plugins/chatgpt_oauth/test_plugin.py +301 -0
  165. code_muse/plugins/chatgpt_oauth/utils.py +538 -0
  166. code_muse/plugins/checkpointing/__init__.py +29 -0
  167. code_muse/plugins/checkpointing/checkpoint_hook.py +51 -0
  168. code_muse/plugins/checkpointing/conversation_snapshots.py +117 -0
  169. code_muse/plugins/checkpointing/register_callbacks.py +51 -0
  170. code_muse/plugins/checkpointing/restore_command.py +263 -0
  171. code_muse/plugins/checkpointing/rewind_shortcut.py +88 -0
  172. code_muse/plugins/checkpointing/shadow_git.py +90 -0
  173. code_muse/plugins/claude_code_hooks/__init__.py +1 -0
  174. code_muse/plugins/claude_code_hooks/config.py +188 -0
  175. code_muse/plugins/claude_code_hooks/register_callbacks.py +208 -0
  176. code_muse/plugins/claude_code_oauth/README.md +167 -0
  177. code_muse/plugins/claude_code_oauth/SETUP.md +93 -0
  178. code_muse/plugins/claude_code_oauth/__init__.py +25 -0
  179. code_muse/plugins/claude_code_oauth/config.py +52 -0
  180. code_muse/plugins/claude_code_oauth/fast_mode.py +124 -0
  181. code_muse/plugins/claude_code_oauth/prompt_handler.py +63 -0
  182. code_muse/plugins/claude_code_oauth/register_callbacks.py +547 -0
  183. code_muse/plugins/claude_code_oauth/test_fast_mode.py +165 -0
  184. code_muse/plugins/claude_code_oauth/test_plugin.py +283 -0
  185. code_muse/plugins/claude_code_oauth/token_refresh_heartbeat.py +237 -0
  186. code_muse/plugins/claude_code_oauth/utils.py +664 -0
  187. code_muse/plugins/copilot_auth/__init__.py +11 -0
  188. code_muse/plugins/copilot_auth/config.py +91 -0
  189. code_muse/plugins/copilot_auth/reasoning_client.py +409 -0
  190. code_muse/plugins/copilot_auth/register_callbacks.py +461 -0
  191. code_muse/plugins/copilot_auth/utils.py +584 -0
  192. code_muse/plugins/custom_commands/__init__.py +14 -0
  193. code_muse/plugins/custom_commands/args_injection.py +82 -0
  194. code_muse/plugins/custom_commands/command_discovery.py +89 -0
  195. code_muse/plugins/custom_commands/command_toml_schema.py +71 -0
  196. code_muse/plugins/custom_commands/register_callbacks.py +176 -0
  197. code_muse/plugins/customizable_commands/__init__.py +0 -0
  198. code_muse/plugins/customizable_commands/register_callbacks.py +136 -0
  199. code_muse/plugins/destructive_command_guard/__init__.py +14 -0
  200. code_muse/plugins/destructive_command_guard/detector.py +375 -0
  201. code_muse/plugins/destructive_command_guard/register_callbacks.py +148 -0
  202. code_muse/plugins/example_custom_command/README.md +280 -0
  203. code_muse/plugins/example_custom_command/register_callbacks.py +51 -0
  204. code_muse/plugins/file_permission_handler/__init__.py +4 -0
  205. code_muse/plugins/file_permission_handler/register_callbacks.py +441 -0
  206. code_muse/plugins/filter_engine/__init__.py +30 -0
  207. code_muse/plugins/filter_engine/classifier.py +153 -0
  208. code_muse/plugins/filter_engine/content_detector.py +184 -0
  209. code_muse/plugins/filter_engine/dispatcher.py +244 -0
  210. code_muse/plugins/filter_engine/register_callbacks.py +188 -0
  211. code_muse/plugins/filter_engine/registry.py +279 -0
  212. code_muse/plugins/filter_engine/strategies/__init__.py +8 -0
  213. code_muse/plugins/filter_engine/strategies/ast_compressor.cpython-314-darwin.so +0 -0
  214. code_muse/plugins/filter_engine/strategies/ast_compressor.cpython-314-x86_64-linux-gnu.so +0 -0
  215. code_muse/plugins/filter_engine/strategies/ast_compressor.pyx +348 -0
  216. code_muse/plugins/filter_engine/strategies/ast_parser.py +167 -0
  217. code_muse/plugins/filter_engine/strategies/code.cpython-314-darwin.so +0 -0
  218. code_muse/plugins/filter_engine/strategies/code.cpython-314-x86_64-linux-gnu.so +0 -0
  219. code_muse/plugins/filter_engine/strategies/code.pyx +584 -0
  220. code_muse/plugins/filter_engine/strategies/git.cpython-314-darwin.so +0 -0
  221. code_muse/plugins/filter_engine/strategies/git.cpython-314-x86_64-linux-gnu.so +0 -0
  222. code_muse/plugins/filter_engine/strategies/git.pyx +438 -0
  223. code_muse/plugins/filter_engine/strategies/json_compressor.cpython-314-darwin.so +0 -0
  224. code_muse/plugins/filter_engine/strategies/json_compressor.pyx +253 -0
  225. code_muse/plugins/filter_engine/strategies/json_patterns.cpython-314-darwin.so +0 -0
  226. code_muse/plugins/filter_engine/strategies/json_patterns.pyx +178 -0
  227. code_muse/plugins/filter_engine/strategies/lint.cpython-314-darwin.so +0 -0
  228. code_muse/plugins/filter_engine/strategies/lint.cpython-314-x86_64-linux-gnu.so +0 -0
  229. code_muse/plugins/filter_engine/strategies/lint.pyx +626 -0
  230. code_muse/plugins/filter_engine/strategies/test.cpython-314-darwin.so +0 -0
  231. code_muse/plugins/filter_engine/strategies/test.cpython-314-x86_64-linux-gnu.so +0 -0
  232. code_muse/plugins/filter_engine/strategies/test.pyx +431 -0
  233. code_muse/plugins/filter_engine/verbosity.py +63 -0
  234. code_muse/plugins/force_push_guard/__init__.py +5 -0
  235. code_muse/plugins/force_push_guard/detector.py +96 -0
  236. code_muse/plugins/force_push_guard/register_callbacks.py +144 -0
  237. code_muse/plugins/force_push_guard/test_detector.py +143 -0
  238. code_muse/plugins/frontend_emitter/__init__.py +25 -0
  239. code_muse/plugins/frontend_emitter/emitter.py +121 -0
  240. code_muse/plugins/frontend_emitter/register_callbacks.py +259 -0
  241. code_muse/plugins/gac/__init__.py +4 -0
  242. code_muse/plugins/gac/git_ops.py +136 -0
  243. code_muse/plugins/gac/prompt.py +191 -0
  244. code_muse/plugins/gac/register_callbacks.py +82 -0
  245. code_muse/plugins/hook_creator/__init__.py +1 -0
  246. code_muse/plugins/hook_creator/register_callbacks.py +34 -0
  247. code_muse/plugins/hook_manager/__init__.py +1 -0
  248. code_muse/plugins/hook_manager/config.py +289 -0
  249. code_muse/plugins/hook_manager/hooks_menu.py +563 -0
  250. code_muse/plugins/hook_manager/register_callbacks.py +227 -0
  251. code_muse/plugins/hook_monitor/register_callbacks.py +36 -0
  252. code_muse/plugins/mindpack/__init__.py +0 -0
  253. code_muse/plugins/mindpack/factory.py +930 -0
  254. code_muse/plugins/mindpack/judge.py +573 -0
  255. code_muse/plugins/mindpack/memory.py +100 -0
  256. code_muse/plugins/mindpack/mindpack_menu.py +1552 -0
  257. code_muse/plugins/mindpack/orchestration.py +605 -0
  258. code_muse/plugins/mindpack/register_callbacks.py +175 -0
  259. code_muse/plugins/mindpack/schemas.py +358 -0
  260. code_muse/plugins/mindpack/tools.py +387 -0
  261. code_muse/plugins/oauth_muse_html.py +226 -0
  262. code_muse/plugins/ollama_setup/__init__.py +5 -0
  263. code_muse/plugins/ollama_setup/completer.py +36 -0
  264. code_muse/plugins/ollama_setup/register_callbacks.py +410 -0
  265. code_muse/plugins/plan_command/__init__.py +0 -0
  266. code_muse/plugins/plan_command/register_callbacks.py +206 -0
  267. code_muse/plugins/plan_mode/__init__.py +37 -0
  268. code_muse/plugins/plan_mode/mode_cycling.py +40 -0
  269. code_muse/plugins/plan_mode/plan_generation.py +68 -0
  270. code_muse/plugins/plan_mode/plan_hooks.py +74 -0
  271. code_muse/plugins/plan_mode/plan_mode_tools.py +138 -0
  272. code_muse/plugins/plan_mode/register_callbacks.py +121 -0
  273. code_muse/plugins/plugin_trust/register_callbacks.py +140 -0
  274. code_muse/plugins/policy_engine/__init__.py +46 -0
  275. code_muse/plugins/policy_engine/approval_flow_integration.py +59 -0
  276. code_muse/plugins/policy_engine/policy_evaluator.py +75 -0
  277. code_muse/plugins/policy_engine/policy_file_discovery.py +90 -0
  278. code_muse/plugins/policy_engine/policy_toml_schema.py +115 -0
  279. code_muse/plugins/policy_engine/register_callbacks.py +112 -0
  280. code_muse/plugins/pop_command/__init__.py +1 -0
  281. code_muse/plugins/pop_command/register_callbacks.py +189 -0
  282. code_muse/plugins/prompt_newline/__init__.py +13 -0
  283. code_muse/plugins/prompt_newline/config.py +19 -0
  284. code_muse/plugins/prompt_newline/register_callbacks.py +159 -0
  285. code_muse/plugins/safety_status/__init__.py +0 -0
  286. code_muse/plugins/safety_status/register_callbacks.py +113 -0
  287. code_muse/plugins/semantic_compression/__init__.py +6 -0
  288. code_muse/plugins/semantic_compression/compressor.py +295 -0
  289. code_muse/plugins/semantic_compression/config.py +123 -0
  290. code_muse/plugins/semantic_compression/register_callbacks.py +320 -0
  291. code_muse/plugins/shell_minimizer/__init__.py +50 -0
  292. code_muse/plugins/shell_minimizer/builtin_filters.toml +393 -0
  293. code_muse/plugins/shell_minimizer/pipeline.py +556 -0
  294. code_muse/plugins/shell_minimizer/primitives.py +482 -0
  295. code_muse/plugins/shell_minimizer/register_callbacks.py +276 -0
  296. code_muse/plugins/shell_safety/__init__.py +6 -0
  297. code_muse/plugins/shell_safety/agent_shell_safety.py +69 -0
  298. code_muse/plugins/shell_safety/command_cache.py +149 -0
  299. code_muse/plugins/shell_safety/register_callbacks.py +202 -0
  300. code_muse/plugins/synthetic_status/__init__.py +1 -0
  301. code_muse/plugins/synthetic_status/register_callbacks.py +128 -0
  302. code_muse/plugins/synthetic_status/status_api.py +145 -0
  303. code_muse/plugins/token_caching/__init__.py +21 -0
  304. code_muse/plugins/token_caching/cache_hit_tracking.py +128 -0
  305. code_muse/plugins/token_caching/cacheable_prefix_detection.py +28 -0
  306. code_muse/plugins/token_caching/register_callbacks.py +54 -0
  307. code_muse/plugins/token_caching/stats_display.py +35 -0
  308. code_muse/plugins/token_tracking/__init__.py +26 -0
  309. code_muse/plugins/token_tracking/database.py +381 -0
  310. code_muse/plugins/token_tracking/edit_analyzer.py +97 -0
  311. code_muse/plugins/token_tracking/record.py +55 -0
  312. code_muse/plugins/token_tracking/register_callbacks.py +277 -0
  313. code_muse/plugins/token_tracking/reports.py +329 -0
  314. code_muse/plugins/universal_constructor/__init__.py +13 -0
  315. code_muse/plugins/universal_constructor/models.py +136 -0
  316. code_muse/plugins/universal_constructor/register_callbacks.py +47 -0
  317. code_muse/plugins/universal_constructor/registry.py +390 -0
  318. code_muse/plugins/universal_constructor/runner.py +474 -0
  319. code_muse/plugins/universal_constructor/safety.py +440 -0
  320. code_muse/plugins/universal_constructor/sandbox.py +584 -0
  321. code_muse/provider_identity.py +105 -0
  322. code_muse/pydantic_patches.py +410 -0
  323. code_muse/reopenable_async_client.py +233 -0
  324. code_muse/round_robin_model.py +151 -0
  325. code_muse/secret_storage.py +74 -0
  326. code_muse/security/__init__.py +1 -0
  327. code_muse/security/redaction.cpython-314-darwin.so +0 -0
  328. code_muse/security/redaction.cpython-314-x86_64-linux-gnu.so +0 -0
  329. code_muse/security/redaction.pyx +135 -0
  330. code_muse/session_storage.py +565 -0
  331. code_muse/status_display.py +261 -0
  332. code_muse/stream_parser/__init__.py +76 -0
  333. code_muse/stream_parser/assistant_text_parser.py +90 -0
  334. code_muse/stream_parser/citation_parser.py +76 -0
  335. code_muse/stream_parser/inline_hidden_tag_parser.py +236 -0
  336. code_muse/stream_parser/proposed_plan_parser.py +158 -0
  337. code_muse/stream_parser/stream_text_chunk.py +23 -0
  338. code_muse/stream_parser/stream_text_parser.py +27 -0
  339. code_muse/stream_parser/tagged_line_parser.cpython-314-darwin.so +0 -0
  340. code_muse/stream_parser/tagged_line_parser.pyx +251 -0
  341. code_muse/stream_parser/utf8_stream_parser.cpython-314-darwin.so +0 -0
  342. code_muse/stream_parser/utf8_stream_parser.pyx +206 -0
  343. code_muse/summarization_agent.py +308 -0
  344. code_muse/terminal_utils.cpython-314-darwin.so +0 -0
  345. code_muse/terminal_utils.cpython-314-x86_64-linux-gnu.so +0 -0
  346. code_muse/terminal_utils.pyx +483 -0
  347. code_muse/tools/__init__.py +459 -0
  348. code_muse/tools/agent_tools.py +613 -0
  349. code_muse/tools/ask_user_question/__init__.py +26 -0
  350. code_muse/tools/ask_user_question/constants.py +73 -0
  351. code_muse/tools/ask_user_question/demo_tui.py +55 -0
  352. code_muse/tools/ask_user_question/handler.py +232 -0
  353. code_muse/tools/ask_user_question/models.py +302 -0
  354. code_muse/tools/ask_user_question/registration.py +37 -0
  355. code_muse/tools/ask_user_question/renderers.py +336 -0
  356. code_muse/tools/ask_user_question/terminal_ui.py +327 -0
  357. code_muse/tools/ask_user_question/theme.py +156 -0
  358. code_muse/tools/ask_user_question/tui_loop.py +422 -0
  359. code_muse/tools/background_jobs.py +99 -0
  360. code_muse/tools/browser/__init__.py +37 -0
  361. code_muse/tools/browser/browser_control.py +289 -0
  362. code_muse/tools/browser/browser_interactions.py +545 -0
  363. code_muse/tools/browser/browser_locators.py +640 -0
  364. code_muse/tools/browser/browser_manager.py +376 -0
  365. code_muse/tools/browser/browser_navigation.py +251 -0
  366. code_muse/tools/browser/browser_screenshot.py +180 -0
  367. code_muse/tools/browser/browser_scripts.py +462 -0
  368. code_muse/tools/browser/browser_workflows.py +222 -0
  369. code_muse/tools/chrome_cdp/__init__.py +1070 -0
  370. code_muse/tools/chrome_cdp/register_callbacks.py +61 -0
  371. code_muse/tools/command_runner.py +1401 -0
  372. code_muse/tools/common.py +1407 -0
  373. code_muse/tools/display.py +87 -0
  374. code_muse/tools/file_modifications.py +1099 -0
  375. code_muse/tools/file_operations.py +860 -0
  376. code_muse/tools/image_tools.py +185 -0
  377. code_muse/tools/meetin_proxy/__init__.py +243 -0
  378. code_muse/tools/meetin_proxy/capture_addon.py +82 -0
  379. code_muse/tools/meetin_proxy/proxy_manager.py +326 -0
  380. code_muse/tools/meetin_proxy/register_callbacks.py +45 -0
  381. code_muse/tools/path_policy.py +219 -0
  382. code_muse/tools/skills_tools.py +586 -0
  383. code_muse/tools/subagent_context.py +158 -0
  384. code_muse/tools/tools_content.py +50 -0
  385. code_muse/tools/universal_constructor.py +965 -0
  386. code_muse/uvx_detection.py +241 -0
  387. code_muse/version_checker.py +86 -0
  388. code_muse-0.0.1.data/data/code_muse/models.json +66 -0
  389. code_muse-0.0.1.data/data/code_muse/models_dev_api.json +1 -0
  390. code_muse-0.0.1.dist-info/METADATA +845 -0
  391. code_muse-0.0.1.dist-info/RECORD +394 -0
  392. code_muse-0.0.1.dist-info/WHEEL +4 -0
  393. code_muse-0.0.1.dist-info/entry_points.txt +2 -0
  394. code_muse-0.0.1.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,268 @@
1
+ """Autonomous Memory Pipeline — callback registrations.
2
+
3
+ Wires the memory pipeline into the Fast Puppy runtime:
4
+ - ``startup``: log memory status on boot
5
+ - ``get_model_system_prompt``: inject memory into system prompts
6
+ - ``custom_command`` / ``custom_command_help``: ``/memory`` slash command
7
+ """
8
+
9
+ import logging
10
+ from datetime import UTC
11
+ from typing import Any
12
+
13
+ from code_muse.callbacks import register_callback
14
+ from code_muse.messaging import emit_error, emit_success, emit_warning
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+ # ---------------------------------------------------------------------------
19
+ # Startup
20
+ # ---------------------------------------------------------------------------
21
+
22
+
23
+ def _setup_cython_hooks() -> None:
24
+ """Enable pyximport so .pyx modules compile on-the-fly."""
25
+ try:
26
+ import pyximport
27
+
28
+ pyximport.install(language_level=3, build_in_temp=True, inplace=True)
29
+ import code_muse
30
+
31
+ if code_muse.CYTHON_ENABLED:
32
+ emit_success(
33
+ f"✅ Cython enabled — {code_muse.PYX_MODULE_COUNT} modules compiled"
34
+ )
35
+ else:
36
+ emit_warning("⚠️ Cython not available — running in pure Python mode")
37
+ except Exception:
38
+ emit_warning("⚠️ Cython not available — running in pure Python mode")
39
+
40
+
41
+ def _on_startup() -> None:
42
+ """Attempt to load memory injection (non-blocking)."""
43
+ try:
44
+ from .memory_injection import load_memory_injection
45
+
46
+ memory = load_memory_injection()
47
+ if memory:
48
+ logger.info("Memory injection available on startup")
49
+ else:
50
+ logger.debug("No fresh memory injection found")
51
+ except Exception as exc:
52
+ logger.warning(f"Memory startup check failed: {exc}")
53
+
54
+
55
+ # ---------------------------------------------------------------------------
56
+ # Startup
57
+ # ---------------------------------------------------------------------------
58
+
59
+
60
+ def _inject_memory_into_prompt(
61
+ model_name: str, default_system_prompt: str, user_prompt: str
62
+ ) -> dict[str, Any | None]:
63
+ """Callback to inject memory into system prompt.
64
+
65
+ Returns ``None`` when no memory is available so the prompt is unchanged.
66
+ Returns ``handled: False`` so other prompt-augmenting callbacks can
67
+ also run (e.g. the agent_skills plugin).
68
+ """
69
+ try:
70
+ from .memory_injection import load_memory_injection
71
+
72
+ memory_text = load_memory_injection()
73
+ if not memory_text:
74
+ return None
75
+
76
+ enhanced = f"{default_system_prompt}\n\n## Memory Guidance\n\n"
77
+ enhanced += (
78
+ "The following is accumulated project knowledge from past sessions.\n"
79
+ "Treat it as heuristic context, not authoritative fact. Always prefer\n"
80
+ "current repo evidence over conflicting memory. Cite the memory path\n"
81
+ "(MEMORY.md) when you use remembered information.\n\n"
82
+ )
83
+ enhanced += memory_text
84
+
85
+ return {
86
+ "instructions": enhanced,
87
+ "user_prompt": user_prompt,
88
+ "handled": False,
89
+ }
90
+ except Exception as exc:
91
+ logger.warning(f"Memory prompt injection failed: {exc}")
92
+ return None
93
+
94
+
95
+ # ---------------------------------------------------------------------------
96
+ # /memory slash command
97
+ # ---------------------------------------------------------------------------
98
+
99
+
100
+ def _memory_command_help() -> list[tuple[str, str]]:
101
+ return [
102
+ ("memory", "Show memory status"),
103
+ ("memory extract", "Trigger extraction now"),
104
+ ("memory forget", "Clear all memory files"),
105
+ ]
106
+
107
+
108
+ def _handle_memory_command(command: str, name: str) -> bool | str | None:
109
+ """Handle ``/memory`` and its subcommands."""
110
+ if name != "memory":
111
+ return None
112
+
113
+ parts = command.strip().split()
114
+ subcommand = parts[1] if len(parts) > 1 else ""
115
+
116
+ if subcommand in ("", "status"):
117
+ return _memory_status()
118
+
119
+ if subcommand == "extract":
120
+ return _memory_extract()
121
+
122
+ if subcommand == "forget":
123
+ return _memory_forget()
124
+
125
+ emit_error(f"Unknown /memory subcommand: {subcommand}")
126
+ return True
127
+
128
+
129
+ def _memory_status() -> str:
130
+ """Display current memory status."""
131
+ try:
132
+ from .session_scanner import (
133
+ get_memory_dir,
134
+ get_project_hash,
135
+ get_sessions_dir,
136
+ scan_eligible_sessions,
137
+ )
138
+
139
+ sessions_dir = get_sessions_dir()
140
+ state_file = sessions_dir / ".memory_state.json"
141
+ eligible = scan_eligible_sessions(sessions_dir, state_file)
142
+
143
+ project_hash = get_project_hash()
144
+ memory_dir = get_memory_dir(project_hash)
145
+ memory_path = memory_dir / "MEMORY.md"
146
+ summary_path = memory_dir / "memory_summary.md"
147
+
148
+ lines: list[str] = ["=== Memory Status ==="]
149
+ lines.append(f"Eligible sessions: {len(eligible)}")
150
+ lines.append(f"Memory dir: {memory_dir}")
151
+ lines.append(f"MEMORY.md exists: {memory_path.exists()}")
152
+ lines.append(f"memory_summary.md exists: {summary_path.exists()}")
153
+
154
+ if summary_path.exists():
155
+ mtime = summary_path.stat().st_mtime
156
+ from datetime import datetime
157
+
158
+ dt = datetime.fromtimestamp(mtime, tz=UTC)
159
+ lines.append(f"Last extraction: {dt.isoformat()}")
160
+
161
+ return "\n".join(lines)
162
+ except Exception as exc:
163
+ logger.error(f"Memory status failed: {exc}")
164
+ return f"Memory status error: {exc}"
165
+
166
+
167
+ def _memory_extract() -> str:
168
+ """Run the full extraction pipeline immediately."""
169
+ try:
170
+ from .consolidation import consolidate_memories, write_memory_files
171
+ from .extraction import extract_session_knowledge
172
+ from .lease_lock import acquire_memory_lease, release_lease
173
+ from .secret_scanner import scan_for_secrets
174
+ from .session_scanner import (
175
+ get_memory_dir,
176
+ get_project_hash,
177
+ get_sessions_dir,
178
+ mark_session_processed,
179
+ scan_eligible_sessions,
180
+ )
181
+
182
+ sessions_dir = get_sessions_dir()
183
+ state_file = sessions_dir / ".memory_state.json"
184
+ eligible = scan_eligible_sessions(sessions_dir, state_file)
185
+
186
+ if not eligible:
187
+ return "No eligible sessions found for extraction."
188
+
189
+ project_hash = get_project_hash()
190
+ memory_dir = get_memory_dir(project_hash)
191
+
192
+ lease = acquire_memory_lease(memory_dir)
193
+ if lease is None:
194
+ return "Memory pipeline is already running (lease held). Try again later."
195
+
196
+ try:
197
+ extractions = []
198
+ for info in eligible:
199
+ result = extract_session_knowledge(info.path)
200
+ if result:
201
+ extractions.append(result)
202
+ mark_session_processed(state_file, str(info.path))
203
+
204
+ if not extractions:
205
+ return "Extraction produced no results."
206
+
207
+ consolidated = consolidate_memories(extractions, sessions_dir)
208
+
209
+ # Safety scan before writing
210
+ secrets = scan_for_secrets(consolidated)
211
+ if secrets:
212
+ secret_names = ", ".join(sorted({s.pattern_name for s in secrets}))
213
+ logger.warning(
214
+ f"Secrets detected in consolidated memory: {secret_names}"
215
+ )
216
+
217
+ memory_path, summary_path = write_memory_files(consolidated, memory_dir)
218
+ return (
219
+ f"Extraction complete: {len(extractions)} sessions, "
220
+ f"MEMORY.md → {memory_path}, summary → {summary_path}"
221
+ )
222
+ finally:
223
+ release_lease(lease)
224
+ except Exception as exc:
225
+ logger.error(f"Memory extraction failed: {exc}")
226
+ return f"Extraction error: {exc}"
227
+
228
+
229
+ def _memory_forget() -> str:
230
+ """Remove all memory files for the current project."""
231
+ try:
232
+ from .session_scanner import get_memory_dir, get_project_hash
233
+
234
+ project_hash = get_project_hash()
235
+ memory_dir = get_memory_dir(project_hash)
236
+
237
+ if not memory_dir.exists():
238
+ return "No memory files to forget."
239
+
240
+ deleted: list[str] = []
241
+ for child in memory_dir.iterdir():
242
+ try:
243
+ if child.is_file():
244
+ child.unlink()
245
+ deleted.append(child.name)
246
+ elif child.is_dir():
247
+ # Don't recursively delete; just report
248
+ deleted.append(f"{child.name}/ (skipped)")
249
+ except OSError as exc:
250
+ logger.warning(f"Could not delete {child}: {exc}")
251
+
252
+ return f"Forgot memory files: {', '.join(deleted) or 'none'}"
253
+ except Exception as exc:
254
+ logger.error(f"Memory forget failed: {exc}")
255
+ return f"Forget error: {exc}"
256
+
257
+
258
+ # ---------------------------------------------------------------------------
259
+ # Register callbacks
260
+ # ---------------------------------------------------------------------------
261
+
262
+ register_callback("startup", _setup_cython_hooks)
263
+ register_callback("startup", _on_startup)
264
+ register_callback("get_model_system_prompt", _inject_memory_into_prompt)
265
+ register_callback("custom_command_help", _memory_command_help)
266
+ register_callback("custom_command", _handle_memory_command)
267
+
268
+ logger.info("Autonomous Memory Pipeline plugin loaded")
@@ -0,0 +1,62 @@
1
+ """Secret scanner for the Autonomous Memory Pipeline.
2
+
3
+ Detects common secret patterns in extracted memory text before it is
4
+ written to disk, helping prevent accidental credential leakage.
5
+ """
6
+
7
+ import logging
8
+ import re
9
+ from dataclasses import dataclass
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+ SCAN_PATTERNS: list[tuple[str, str]] = [
14
+ ("aws_access_key", r"AKIA[0-9A-Z]{16}"),
15
+ ("github_token", r"ghp_[0-9a-zA-Z]{36}"),
16
+ ("openai_api_key", r"sk-[0-9a-zA-Z]{48}"),
17
+ (
18
+ "private_key_header",
19
+ r"-----BEGIN (RSA|EC|OPENSSH|PGP) PRIVATE KEY-----",
20
+ ),
21
+ (
22
+ "jwt_token",
23
+ r"eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}",
24
+ ),
25
+ (
26
+ "generic_api_key",
27
+ r"(api[_-]?key|apikey|secret|token|password)\s*[:=]\s*['\"]?[A-Za-z0-9+/=]{20,}['\"]?",
28
+ ),
29
+ ]
30
+
31
+
32
+ @dataclass
33
+ class SecretMatch:
34
+ """A single secret pattern match."""
35
+
36
+ pattern_name: str
37
+ line_number: int
38
+ context: str
39
+
40
+
41
+ def scan_for_secrets(text: str) -> list[SecretMatch]:
42
+ """Scan ``text`` for known secret patterns.
43
+
44
+ Returns a list of :class:`SecretMatch` objects with line numbers and
45
+ a short context snippet (first 40 characters of the match).
46
+ """
47
+ matches: list[SecretMatch] = []
48
+ lines = text.splitlines()
49
+
50
+ for pattern_name, pattern in SCAN_PATTERNS:
51
+ for line_idx, line in enumerate(lines, start=1):
52
+ for match in re.finditer(pattern, line, re.IGNORECASE):
53
+ context = match.group(0)[:40]
54
+ matches.append(
55
+ SecretMatch(
56
+ pattern_name=pattern_name,
57
+ line_number=line_idx,
58
+ context=context,
59
+ )
60
+ )
61
+
62
+ return matches
@@ -0,0 +1,163 @@
1
+ """Session scanner for the Autonomous Memory Pipeline.
2
+
3
+ Discovers eligible past sessions based on message count, activity status,
4
+ and idle time. Tracks processed sessions via a persistent state file.
5
+ """
6
+
7
+ import hashlib
8
+ import json
9
+ import logging
10
+ import os
11
+ import time
12
+ from dataclasses import dataclass
13
+ from pathlib import Path
14
+ from typing import Any
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+ IDLE_THRESHOLD_SECONDS = 10_800 # 3 hours
19
+ ACTIVE_THRESHOLD_SECONDS = 1_800 # 30 minutes
20
+ MIN_MESSAGE_COUNT = 10
21
+
22
+
23
+ @dataclass
24
+ class SessionInfo:
25
+ """Summary of a discovered session directory."""
26
+
27
+ path: Path
28
+ message_count: int
29
+ last_active: float
30
+ is_active: bool
31
+ processed: bool
32
+
33
+
34
+ def _find_messages_file(session_dir: Path) -> Path | None:
35
+ """Locate the messages file (messages.json or any .jsonl) in a session dir."""
36
+ candidates = [
37
+ session_dir / "messages.json",
38
+ *session_dir.glob("*.jsonl"),
39
+ ]
40
+ for candidate in candidates:
41
+ if candidate.exists():
42
+ return candidate
43
+ return None
44
+
45
+
46
+ def _count_user_messages(messages_path: Path) -> int:
47
+ """Count lines that appear to contain a user message."""
48
+ try:
49
+ with messages_path.open("r", encoding="utf-8") as fh:
50
+ return sum(
51
+ 1 for line in fh if '"role": "user"' in line or '"role":"user"' in line
52
+ )
53
+ except Exception as exc:
54
+ logger.warning(f"Failed to count messages in {messages_path}: {exc}")
55
+ return 0
56
+
57
+
58
+ def _read_state(state_file: Path) -> dict[str, Any]:
59
+ """Read the processed-sessions state file."""
60
+ if not state_file.exists():
61
+ return {"processed": []}
62
+ try:
63
+ with state_file.open("r", encoding="utf-8") as fh:
64
+ data = json.load(fh)
65
+ if isinstance(data, dict) and isinstance(data.get("processed"), list):
66
+ return data
67
+ except Exception as exc:
68
+ logger.warning(f"Corrupt state file {state_file}: {exc}")
69
+ return {"processed": []}
70
+
71
+
72
+ def _write_state(state_file: Path, data: dict[str, Any]) -> None:
73
+ """Persist the processed-sessions state file."""
74
+ state_file.parent.mkdir(parents=True, exist_ok=True)
75
+ with state_file.open("w", encoding="utf-8") as fh:
76
+ json.dump(data, fh, indent=2)
77
+
78
+
79
+ def scan_eligible_sessions(sessions_dir: Path, state_file: Path) -> list[SessionInfo]:
80
+ """Scan for sessions that are eligible for memory extraction.
81
+
82
+ Filters:
83
+ - message_count >= 10
84
+ - not currently active
85
+ - idle > 3 hours
86
+ - not already processed
87
+
88
+ Results are sorted by ``last_active`` descending (most recent first).
89
+ """
90
+ if not sessions_dir.exists():
91
+ return []
92
+
93
+ state = _read_state(state_file)
94
+ processed_paths: set[str] = set(state.get("processed", []))
95
+ now = time.time()
96
+ sessions: list[SessionInfo] = []
97
+
98
+ for entry in sessions_dir.iterdir():
99
+ if not entry.is_dir():
100
+ continue
101
+
102
+ messages_file = _find_messages_file(entry)
103
+ if messages_file is None:
104
+ continue
105
+
106
+ msg_count = _count_user_messages(messages_file)
107
+ last_active = messages_file.stat().st_mtime
108
+ idle_seconds = now - last_active
109
+
110
+ # Active = has a lock file OR recent mtime (< 30 min)
111
+ lock_file = entry / ".session_lock"
112
+ is_active = lock_file.exists() or idle_seconds < ACTIVE_THRESHOLD_SECONDS
113
+
114
+ session_path_str = str(entry)
115
+ processed = session_path_str in processed_paths
116
+
117
+ if msg_count < MIN_MESSAGE_COUNT:
118
+ continue
119
+ if is_active:
120
+ continue
121
+ if idle_seconds <= IDLE_THRESHOLD_SECONDS:
122
+ continue
123
+ if processed:
124
+ continue
125
+
126
+ sessions.append(
127
+ SessionInfo(
128
+ path=entry,
129
+ message_count=msg_count,
130
+ last_active=last_active,
131
+ is_active=is_active,
132
+ processed=processed,
133
+ )
134
+ )
135
+
136
+ sessions.sort(key=lambda s: s.last_active, reverse=True)
137
+ return sessions
138
+
139
+
140
+ def mark_session_processed(state_file: Path, session_path: str) -> None:
141
+ """Record a session as processed so it won't be re-scanned."""
142
+ state = _read_state(state_file)
143
+ processed: list[str] = state.get("processed", [])
144
+ if session_path not in processed:
145
+ processed.append(session_path)
146
+ state["processed"] = processed
147
+ _write_state(state_file, state)
148
+
149
+
150
+ def get_sessions_dir() -> Path:
151
+ """Return the global sessions storage directory."""
152
+ return Path.home() / ".muse" / "sessions"
153
+
154
+
155
+ def get_memory_dir(project_hash: str) -> Path:
156
+ """Return the memory storage directory for a given project hash."""
157
+ return Path.home() / ".muse" / "memory" / project_hash
158
+
159
+
160
+ def get_project_hash(cwd: str | None = None) -> str:
161
+ """Compute a 12-char project hash from the working directory."""
162
+ cwd = cwd or os.getcwd()
163
+ return hashlib.sha256(cwd.encode()).hexdigest()[:12]
@@ -0,0 +1,14 @@
1
+ """AWS Bedrock Plugin for Muse.
2
+
3
+ This plugin enables Muse to use Anthropic Claude models hosted on
4
+ AWS Bedrock with standard AWS credential chain authentication (env vars,
5
+ profiles, IAM roles, SSO).
6
+
7
+ Supported models:
8
+ - Claude Opus 4.7 (1M context)
9
+ - Claude Opus 4.6 (1M context)
10
+ - Claude Sonnet 4.6 (1M context)
11
+ - Claude Haiku 4.5 (200K context)
12
+ """
13
+
14
+ __version__ = "0.1.0"
@@ -0,0 +1,99 @@
1
+ """Configuration constants for the AWS Bedrock plugin."""
2
+
3
+ import functools
4
+ import os
5
+ from pathlib import Path
6
+
7
+ from code_muse.config import DATA_DIR
8
+
9
+ ENV_AWS_REGION = "AWS_REGION"
10
+ ENV_AWS_PROFILE = "AWS_PROFILE"
11
+ ENV_BEDROCK_REGION = "BEDROCK_REGION"
12
+
13
+ DEFAULT_REGION = "us-east-1"
14
+
15
+
16
+ @functools.lru_cache(maxsize=1)
17
+ def _detect_region() -> str | None:
18
+ """Detect region from boto3 session or EC2 instance metadata.
19
+
20
+ Result is cached for the process lifetime: on non-EC2 hosts with no
21
+ AWS config, the IMDS path below incurs two 1-second timeouts, and
22
+ this function is called on every model instantiation.
23
+ """
24
+ try:
25
+ import boto3
26
+
27
+ region = boto3.Session().region_name
28
+ if region:
29
+ return region
30
+ except Exception:
31
+ pass
32
+
33
+ try:
34
+ import urllib.request
35
+
36
+ token_req = urllib.request.Request(
37
+ "http://169.254.169.254/latest/api/token",
38
+ method="PUT",
39
+ headers={"X-aws-ec2-metadata-token-ttl-seconds": "30"},
40
+ )
41
+ token = urllib.request.urlopen(token_req, timeout=1).read().decode()
42
+ region_req = urllib.request.Request(
43
+ "http://169.254.169.254/latest/meta-data/placement/region",
44
+ headers={"X-aws-ec2-metadata-token": token},
45
+ )
46
+ return urllib.request.urlopen(region_req, timeout=1).read().decode()
47
+ except Exception:
48
+ return None
49
+
50
+
51
+ MODELS: list[dict] = [
52
+ {
53
+ "base_key": "bedrock-opus-4-7",
54
+ "model_id": "us.anthropic.claude-opus-4-7",
55
+ "context_length": 1000000,
56
+ "variants": ["default", "low", "medium", "high", "xhigh", "max"],
57
+ },
58
+ {
59
+ "base_key": "bedrock-opus-4-6",
60
+ "model_id": "us.anthropic.claude-opus-4-6-v1:0",
61
+ "context_length": 1000000,
62
+ "variants": ["default", "low", "medium", "high", "max"],
63
+ },
64
+ {
65
+ "base_key": "bedrock-sonnet-4-6",
66
+ "model_id": "us.anthropic.claude-sonnet-4-6-v1:0",
67
+ "context_length": 1000000,
68
+ "variants": ["default", "low", "medium", "high", "max"],
69
+ },
70
+ {
71
+ "base_key": "bedrock-haiku",
72
+ "model_id": "us.anthropic.claude-haiku-4-5-20251001-v1:0",
73
+ "context_length": 200000,
74
+ "variants": None,
75
+ },
76
+ ]
77
+
78
+
79
+ def get_bedrock_region() -> str:
80
+ """Get the AWS region for Bedrock.
81
+
82
+ Precedence: BEDROCK_REGION > AWS_REGION > boto3 session > default.
83
+ """
84
+ return (
85
+ os.environ.get(ENV_BEDROCK_REGION)
86
+ or os.environ.get(ENV_AWS_REGION)
87
+ or _detect_region()
88
+ or DEFAULT_REGION
89
+ )
90
+
91
+
92
+ def get_aws_profile() -> str | None:
93
+ """Get the AWS profile name from environment."""
94
+ return os.environ.get(ENV_AWS_PROFILE)
95
+
96
+
97
+ def get_extra_models_path() -> Path:
98
+ """Get the path to the extra_models.json file."""
99
+ return Path(DATA_DIR) / "extra_models.json"