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,68 @@
1
+ """Plan document generation and persistence.
2
+
3
+ Produces markdown plan files with YAML front-matter and saves them to a
4
+ local ``plans/`` directory.
5
+ """
6
+
7
+ from datetime import UTC, datetime
8
+ from pathlib import Path
9
+
10
+
11
+ def generate_plan_md(
12
+ goal: str,
13
+ research_notes: str,
14
+ discussion: str,
15
+ steps: list[str],
16
+ ) -> str:
17
+ """Generate a markdown plan document with YAML front-matter.
18
+
19
+ Args:
20
+ goal: Short title/summary of the plan.
21
+ research_notes: Analysis and context gathered during planning.
22
+ discussion: Reasoning and trade-offs considered.
23
+ steps: Ordered list of implementation steps.
24
+
25
+ Returns:
26
+ A fully formatted markdown string.
27
+ """
28
+ ts = datetime.now(UTC).isoformat()
29
+ numbered_steps = "\n".join(f"{i + 1}. {step}" for i, step in enumerate(steps))
30
+ return f"""---
31
+ goal: "{goal}"
32
+ created_at: "{ts}"
33
+ status: draft
34
+ ---
35
+
36
+ # Plan: {goal}
37
+
38
+ ## Analysis
39
+ {research_notes}
40
+
41
+ ## Discussion
42
+ {discussion}
43
+
44
+ ## Implementation Steps
45
+ {numbered_steps}
46
+
47
+ ## Risks
48
+ - (placeholder)
49
+ """
50
+
51
+
52
+ def save_plan(content: str, plans_dir: Path = Path("plans")) -> Path:
53
+ """Save plan markdown to ``plans/plan_{timestamp}.md``.
54
+
55
+ Creates the directory if it does not exist.
56
+
57
+ Args:
58
+ content: Markdown content to save.
59
+ plans_dir: Target directory (default: ``plans/`` in cwd).
60
+
61
+ Returns:
62
+ The path to the written file.
63
+ """
64
+ plans_dir.mkdir(parents=True, exist_ok=True)
65
+ ts = datetime.now(UTC).strftime("%Y%m%d_%H%M%S")
66
+ file_path = plans_dir / f"plan_{ts}.md"
67
+ file_path.write_text(content, encoding="utf-8")
68
+ return file_path
@@ -0,0 +1,74 @@
1
+ """Plan mode enforcement hook.
2
+
3
+ Registers a ``pre_tool_call`` callback that blocks destructive tools
4
+ according to the active plan mode state.
5
+ """
6
+
7
+ import logging
8
+ from typing import Any
9
+
10
+ from code_muse.plugins.plan_mode.plan_mode_tools import PlanModeState, get_current_mode
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+ # Tools allowed in PLAN mode (research / read-only / meta)
15
+ _PLAN_ALLOWED_TOOLS: set[str] = {
16
+ "read_file",
17
+ "list_files",
18
+ "grep",
19
+ "ask_user_question",
20
+ "list_or_search_skills",
21
+ "enter_plan_mode",
22
+ "exit_plan_mode",
23
+ "get_plan_mode",
24
+ "approve_plan",
25
+ "cancel_plan",
26
+ "open_plan_in_editor",
27
+ }
28
+
29
+ # Tools blocked in AUTO_EDIT mode (shell commands only)
30
+ _AUTO_EDIT_BLOCKED_TOOLS: set[str] = {
31
+ "agent_run_shell_command",
32
+ "run_shell_command",
33
+ }
34
+
35
+
36
+ async def plan_mode_pre_tool_call_hook(
37
+ tool_name: str, tool_args: dict, context: Any = None
38
+ ) -> dict | None:
39
+ """Enforce mode-specific tool restrictions.
40
+
41
+ Returns:
42
+ ``{"blocked": True, "error_message": "..."}`` if the tool is
43
+ blocked in the current mode, otherwise ``None``.
44
+ """
45
+ mode = get_current_mode()
46
+
47
+ if mode == PlanModeState.DEFAULT:
48
+ return None
49
+
50
+ if mode == PlanModeState.PLAN:
51
+ if tool_name in _PLAN_ALLOWED_TOOLS:
52
+ return None
53
+ logger.info("Blocked tool '%s' during plan mode", tool_name)
54
+ return {
55
+ "blocked": True,
56
+ "error_message": (
57
+ "🚫 Plan mode is active. This tool is blocked during planning. "
58
+ "Use exit_plan_mode to return to normal mode."
59
+ ),
60
+ }
61
+
62
+ if mode == PlanModeState.AUTO_EDIT:
63
+ if tool_name in _AUTO_EDIT_BLOCKED_TOOLS:
64
+ logger.info("Blocked tool '%s' during auto-edit mode", tool_name)
65
+ return {
66
+ "blocked": True,
67
+ "error_message": (
68
+ "🚫 Auto-edit mode is active. Shell commands are blocked. "
69
+ "Use exit_plan_mode to return to normal mode."
70
+ ),
71
+ }
72
+ return None
73
+
74
+ return None
@@ -0,0 +1,138 @@
1
+ """Plan mode state management and tool registration.
2
+
3
+ Tools:
4
+ - enter_plan_mode(goal: str = "")
5
+ - exit_plan_mode()
6
+ - get_plan_mode()
7
+ - approve_plan()
8
+ - open_plan_in_editor()
9
+ """
10
+
11
+ import enum
12
+ import os
13
+ import subprocess
14
+ from pathlib import Path
15
+ from typing import Any
16
+
17
+ from pydantic_ai import RunContext
18
+
19
+ from code_muse.messaging.bus import get_message_bus
20
+
21
+
22
+ class PlanModeState(enum.Enum):
23
+ """Three-state mode model."""
24
+
25
+ DEFAULT = "default"
26
+ AUTO_EDIT = "auto_edit"
27
+ PLAN = "plan"
28
+
29
+
30
+ # Module-level mutable state (integration note: if the agent runtime ever
31
+ # exposes a formal ``_mode`` attribute, mirror state there).
32
+ _current_mode: PlanModeState = PlanModeState.DEFAULT
33
+ _plan_goal: str = ""
34
+
35
+
36
+ def get_current_mode() -> PlanModeState:
37
+ """Return the active plan mode state."""
38
+ return _current_mode
39
+
40
+
41
+ def get_plan_goal() -> str:
42
+ """Return the current planning goal, if any."""
43
+ return _plan_goal
44
+
45
+
46
+ def set_plan_mode(mode: PlanModeState, goal: str = "") -> None:
47
+ """Update the global plan mode state and optional goal."""
48
+ global _current_mode, _plan_goal
49
+ _current_mode = mode
50
+ _plan_goal = goal
51
+
52
+
53
+ def register_enter_plan_mode(agent: Any) -> None:
54
+ """Register the ``enter_plan_mode`` tool on *agent*."""
55
+
56
+ @agent.tool
57
+ def enter_plan_mode(context: RunContext, goal: str = "") -> str:
58
+ """Enter plan mode. Optional goal describes what you are planning.
59
+
60
+ In plan mode, destructive tools (write_file, replace_in_file,
61
+ delete_file, shell commands) are blocked. Research tools
62
+ (read_file, list_files, grep, ask_user_question) remain available.
63
+ """
64
+ set_plan_mode(PlanModeState.PLAN, goal)
65
+ bus = get_message_bus()
66
+ bus.emit_info(f"📋 Plan mode activated{f' (goal: {goal})' if goal else ''}")
67
+ return f"Plan mode active. Goal: {goal or '(none)'}"
68
+
69
+
70
+ def register_exit_plan_mode(agent: Any) -> None:
71
+ """Register the ``exit_plan_mode`` tool on *agent*."""
72
+
73
+ @agent.tool
74
+ def exit_plan_mode(context: RunContext) -> str:
75
+ """Exit plan mode and return to normal operation."""
76
+ set_plan_mode(PlanModeState.DEFAULT)
77
+ bus = get_message_bus()
78
+ bus.emit_info("📋 Plan mode deactivated — normal editing resumed")
79
+ return "Plan mode deactivated. Normal editing resumed."
80
+
81
+
82
+ def register_get_plan_mode(agent: Any) -> None:
83
+ """Register the ``get_plan_mode`` tool on *agent*."""
84
+
85
+ @agent.tool
86
+ def get_plan_mode(context: RunContext) -> str:
87
+ """Return the current plan mode state and goal."""
88
+ mode = get_current_mode()
89
+ goal = get_plan_goal()
90
+ return f"Mode: {mode.value} | Goal: {goal or '(none)'}"
91
+
92
+
93
+ def register_approve_plan(agent: Any) -> None:
94
+ """Register the ``approve_plan`` tool on *agent*."""
95
+
96
+ @agent.tool
97
+ def approve_plan(context: RunContext) -> str:
98
+ """Approve the current plan and enter auto-edit mode."""
99
+ set_plan_mode(PlanModeState.AUTO_EDIT)
100
+ bus = get_message_bus()
101
+ bus.emit_info("✅ Plan approved — entering auto-edit mode")
102
+ return "Plan approved. Auto-edit mode active."
103
+
104
+
105
+ def register_open_plan_in_editor(agent: Any) -> None:
106
+ """Register the ``open_plan_in_editor`` tool on *agent*."""
107
+
108
+ @agent.tool
109
+ def open_plan_in_editor(context: RunContext) -> str:
110
+ """Open the most recent plan file in the default editor."""
111
+ plans_dir = Path("plans")
112
+ if not plans_dir.exists():
113
+ return "Error: plans directory not found."
114
+
115
+ plan_files = sorted(
116
+ plans_dir.glob("*.md"),
117
+ key=lambda p: p.stat().st_mtime,
118
+ reverse=True,
119
+ )
120
+ if not plan_files:
121
+ return "Error: no plan files found."
122
+
123
+ plan_path = plan_files[0]
124
+
125
+ candidates = []
126
+ editor = os.environ.get("EDITOR")
127
+ if editor:
128
+ candidates.append(editor)
129
+ candidates.extend(["nvim", "vim", "nano"])
130
+
131
+ for editor in candidates:
132
+ try:
133
+ subprocess.run([editor, str(plan_path)], check=False)
134
+ return f"Opened {plan_path} in {editor}"
135
+ except FileNotFoundError:
136
+ continue
137
+
138
+ return "Error: no suitable editor found (tried $EDITOR, nvim, vim, nano)."
@@ -0,0 +1,121 @@
1
+ """Callback registration for the Plan Mode plugin.
2
+
3
+ Registers:
4
+ - ``enter_plan_mode``, ``exit_plan_mode``, ``get_plan_mode`` tools
5
+ - ``pre_tool_call`` enforcement hook
6
+ - ``/plan [goal]``, ``/plan exit`` slash commands
7
+ - ``/mode`` slash command (delegated to :mod:`mode_cycling`)
8
+ - Help entries for all slash commands
9
+ """
10
+
11
+ import logging
12
+ from typing import Any
13
+
14
+ from code_muse.callbacks import register_callback
15
+ from code_muse.messaging import emit_info, emit_success
16
+ from code_muse.plugins.plan_mode.mode_cycling import cycle_mode
17
+ from code_muse.plugins.plan_mode.plan_hooks import plan_mode_pre_tool_call_hook
18
+ from code_muse.plugins.plan_mode.plan_mode_tools import (
19
+ PlanModeState,
20
+ get_current_mode,
21
+ register_approve_plan,
22
+ register_enter_plan_mode,
23
+ register_exit_plan_mode,
24
+ register_get_plan_mode,
25
+ register_open_plan_in_editor,
26
+ set_plan_mode,
27
+ )
28
+
29
+ logger = logging.getLogger(__name__)
30
+
31
+
32
+ # ---------------------------------------------------------------------------
33
+ # Tool registration callback
34
+ # ---------------------------------------------------------------------------
35
+
36
+
37
+ def _register_plan_mode_tools() -> list[dict[str, Any]]:
38
+ """Return tool definitions for the plan mode plugin."""
39
+ return [
40
+ {"name": "enter_plan_mode", "register_func": register_enter_plan_mode},
41
+ {"name": "exit_plan_mode", "register_func": register_exit_plan_mode},
42
+ {"name": "get_plan_mode", "register_func": register_get_plan_mode},
43
+ {"name": "approve_plan", "register_func": register_approve_plan},
44
+ {"name": "open_plan_in_editor", "register_func": register_open_plan_in_editor},
45
+ ]
46
+
47
+
48
+ # ---------------------------------------------------------------------------
49
+ # Slash-command handlers
50
+ # ---------------------------------------------------------------------------
51
+
52
+
53
+ def _on_custom_command(command: str, name: str) -> bool | None:
54
+ """Handle ``/plan``, ``/plan exit``, and ``/mode`` commands."""
55
+ if name == "mode":
56
+ return _handle_mode_command(command)
57
+
58
+ if name == "plan":
59
+ return _handle_plan_command(command)
60
+
61
+ return None
62
+
63
+
64
+ def _handle_plan_command(command: str) -> bool:
65
+ """Handle ``/plan [goal]`` and ``/plan exit``."""
66
+ parts = command.split(maxsplit=1)
67
+ remainder = parts[1].strip() if len(parts) > 1 else ""
68
+
69
+ if remainder.lower() == "exit":
70
+ set_plan_mode(PlanModeState.DEFAULT)
71
+ emit_success("📋 Plan mode exited — normal editing resumed")
72
+ return True
73
+
74
+ # remainder is the optional goal (may be empty)
75
+ set_plan_mode(PlanModeState.PLAN, remainder)
76
+ if remainder:
77
+ emit_success(f"📋 Plan mode active — goal: {remainder}")
78
+ else:
79
+ emit_success("📋 Plan mode active")
80
+ return True
81
+
82
+
83
+ def _handle_mode_command(command: str) -> bool:
84
+ """Handle ``/mode`` — cycle through DEFAULT → AUTO_EDIT → PLAN."""
85
+ old_mode = get_current_mode()
86
+ new_mode = cycle_mode()
87
+ mode_names = {
88
+ PlanModeState.DEFAULT: "Default",
89
+ PlanModeState.AUTO_EDIT: "Auto-edit",
90
+ PlanModeState.PLAN: "Plan",
91
+ }
92
+ emit_info(
93
+ f"🔁 Mode changed: {mode_names.get(old_mode, old_mode.value)} → "
94
+ f"{mode_names.get(new_mode, new_mode.value)}"
95
+ )
96
+ return True
97
+
98
+
99
+ # ---------------------------------------------------------------------------
100
+ # Help entries
101
+ # ---------------------------------------------------------------------------
102
+
103
+
104
+ def _on_custom_command_help() -> list[tuple[str, str]]:
105
+ return [
106
+ ("plan", "Enter plan mode (optionally with a goal)"),
107
+ ("plan exit", "Exit plan mode and resume normal editing"),
108
+ ("mode", "Cycle through DEFAULT → AUTO_EDIT → PLAN mode"),
109
+ ]
110
+
111
+
112
+ # ---------------------------------------------------------------------------
113
+ # Register all callbacks
114
+ # ---------------------------------------------------------------------------
115
+
116
+ register_callback("register_tools", _register_plan_mode_tools)
117
+ register_callback("pre_tool_call", plan_mode_pre_tool_call_hook)
118
+ register_callback("custom_command", _on_custom_command)
119
+ register_callback("custom_command_help", _on_custom_command_help)
120
+
121
+ logger.debug("Plan Mode plugin callbacks registered")
@@ -0,0 +1,140 @@
1
+ """Builtin plugin trust command handler.
2
+
3
+ Implements /plugin list, /plugin trust <name>, /plugin revoke <name>,
4
+ and /plugin help.
5
+ """
6
+
7
+ import logging
8
+
9
+ from code_muse.callbacks import register_callback
10
+ from code_muse.plugins import (
11
+ _PLUGINS_LOADED,
12
+ compute_plugin_hash,
13
+ get_user_plugins_dir,
14
+ is_plugin_trusted,
15
+ record_plugin_trust,
16
+ revoke_plugin_trust,
17
+ )
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+
22
+ def _plugin_command_help() -> list[tuple[str, str]]:
23
+ """Return help entries for plugin trust commands."""
24
+ return [
25
+ ("plugin list", "List discovered user plugins with trust status"),
26
+ ("plugin trust <name>", "Trust a user plugin by content hash"),
27
+ ("plugin revoke <name>", "Revoke trust for a user plugin"),
28
+ ("plugin help", "Show plugin trust command help"),
29
+ ]
30
+
31
+
32
+ def _handle_plugin_command(command: str, name: str) -> str | None:
33
+ """Handle /plugin subcommands."""
34
+ if name != "plugin":
35
+ return None
36
+
37
+ parts = command.strip().split()
38
+ if len(parts) < 2:
39
+ return _plugin_help_text()
40
+
41
+ sub = parts[1].lower()
42
+
43
+ if sub == "help":
44
+ return _plugin_help_text()
45
+
46
+ if sub in ("list", "plugins"):
47
+ return _plugin_list()
48
+
49
+ if sub == "trust":
50
+ if len(parts) < 3:
51
+ return "Usage: /plugin trust <plugin_name>"
52
+ return _plugin_trust(parts[2])
53
+
54
+ if sub == "revoke":
55
+ if len(parts) < 3:
56
+ return "Usage: /plugin revoke <plugin_name>"
57
+ return _plugin_revoke(parts[2])
58
+
59
+ return f"Unknown /plugin subcommand: {sub}. Usage: /plugin trust|revoke|list|help"
60
+
61
+
62
+ def _plugin_help_text() -> str:
63
+ return (
64
+ "Plugin trust commands:\n"
65
+ " /plugin list — List discovered user plugins with hash & trust status\n"
66
+ " /plugin trust <name> — Record trust for a user plugin\n"
67
+ " /plugin revoke <name> — Revoke trust for a user plugin\n"
68
+ " /plugin help — Show this help"
69
+ )
70
+
71
+
72
+ def _plugin_list() -> str:
73
+ """Summarise discovered user plugins with hash and trust status."""
74
+ user_dir = get_user_plugins_dir()
75
+ if not user_dir.exists():
76
+ return f"No user plugins directory found at {user_dir}"
77
+
78
+ entries: list[str] = []
79
+ for item in sorted(user_dir.iterdir()):
80
+ if not item.is_dir() or item.name.startswith(".") or item.name.startswith("_"):
81
+ continue
82
+
83
+ has_callbacks = (item / "register_callbacks.py").exists()
84
+ has_init = (item / "__init__.py").exists()
85
+ if not has_callbacks and not has_init:
86
+ continue
87
+
88
+ try:
89
+ content_hash = compute_plugin_hash(item)
90
+ except OSError as exc:
91
+ logger.warning("Failed to hash plugin %s: %s", item.name, exc)
92
+ continue
93
+
94
+ trusted = is_plugin_trusted(item.name, content_hash)
95
+ status = "trusted" if trusted else "untrusted"
96
+ entries.append(f" {item.name:20} {status:10} {content_hash[:12]}… {item}")
97
+
98
+ if not entries:
99
+ return f"No user plugins found in {user_dir}"
100
+
101
+ header = f"{'Name':20} {'Status':10} {'Hash':14} Path"
102
+ return "User plugins:\n" + header + "\n" + "\n".join(entries)
103
+
104
+
105
+ def _plugin_trust(plugin_name: str) -> str:
106
+ """Compute current hash and record trust for a user plugin."""
107
+ user_dir = get_user_plugins_dir()
108
+ plugin_dir = user_dir / plugin_name
109
+
110
+ if not plugin_dir.exists() or not plugin_dir.is_dir():
111
+ return f"Plugin '{plugin_name}' not found in {user_dir}"
112
+
113
+ if (
114
+ not (plugin_dir / "register_callbacks.py").exists()
115
+ and not (plugin_dir / "__init__.py").exists()
116
+ ):
117
+ return f"Plugin '{plugin_name}' has no register_callbacks.py or __init__.py"
118
+
119
+ try:
120
+ content_hash = compute_plugin_hash(plugin_dir)
121
+ except OSError as exc:
122
+ logger.warning("Failed to hash plugin %s: %s", plugin_name, exc)
123
+ return f"Failed to compute hash for plugin '{plugin_name}'"
124
+
125
+ record_plugin_trust(plugin_name, content_hash, str(plugin_dir))
126
+
127
+ msg = f"Trust recorded for plugin '{plugin_name}' (hash: {content_hash[:12]}…)."
128
+ if _PLUGINS_LOADED:
129
+ msg += " Restart Muse to load newly trusted plugins."
130
+ return msg
131
+
132
+
133
+ def _plugin_revoke(plugin_name: str) -> str:
134
+ """Revoke trust for a user plugin."""
135
+ revoke_plugin_trust(plugin_name)
136
+ return f"Trust revoked for plugin '{plugin_name}'. Restart Muse to unload."
137
+
138
+
139
+ register_callback("custom_command", _handle_plugin_command)
140
+ register_callback("custom_command_help", _plugin_command_help)
@@ -0,0 +1,46 @@
1
+ """Policy Engine plugin for Muse.
2
+
3
+ TOML-based rules that control tool execution:
4
+ - allow → auto-approve (skip confirmation)
5
+ - deny → block with message
6
+ - ask_user → show normal confirmation dialog
7
+
8
+ Rules match by toolName (with * wildcard) and optional commandPrefix
9
+ (for shell commands). Rules are loaded from:
10
+ - User tier: ~/.muse/policies/*.toml
11
+ - Project tier: .muse/policies/*.toml
12
+
13
+ Priority resolves conflicts (higher wins).
14
+ """
15
+
16
+ from code_muse.plugins.policy_engine.approval_flow_integration import (
17
+ integrate_policy_check,
18
+ )
19
+ from code_muse.plugins.policy_engine.policy_evaluator import (
20
+ evaluate_policy,
21
+ evaluate_tool_policy,
22
+ )
23
+ from code_muse.plugins.policy_engine.policy_file_discovery import (
24
+ clear_policy_cache,
25
+ discover_policy_files,
26
+ load_all_policies,
27
+ )
28
+ from code_muse.plugins.policy_engine.policy_toml_schema import (
29
+ Decision,
30
+ ToolRule,
31
+ parse_policy_toml,
32
+ validate_rules,
33
+ )
34
+
35
+ __all__ = [
36
+ "Decision",
37
+ "ToolRule",
38
+ "clear_policy_cache",
39
+ "discover_policy_files",
40
+ "evaluate_policy",
41
+ "evaluate_tool_policy",
42
+ "integrate_policy_check",
43
+ "load_all_policies",
44
+ "parse_policy_toml",
45
+ "validate_rules",
46
+ ]
@@ -0,0 +1,59 @@
1
+ import logging
2
+ from typing import Any
3
+
4
+ from code_muse.plugins.policy_engine.policy_evaluator import (
5
+ evaluate_policy,
6
+ evaluate_tool_policy,
7
+ )
8
+ from code_muse.plugins.policy_engine.policy_file_discovery import load_all_policies
9
+ from code_muse.plugins.policy_engine.policy_toml_schema import Decision, ToolRule
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+ def integrate_policy_check(
15
+ tool_name: str,
16
+ command: str | None = None,
17
+ rules: list[ToolRule] | None = None,
18
+ ) -> dict[str, Any] | None:
19
+ if rules is None:
20
+ rules = load_all_policies()
21
+
22
+ if command is not None:
23
+ decision, matched_rule = evaluate_policy(tool_name, command, rules)
24
+ else:
25
+ decision, matched_rule = evaluate_tool_policy(tool_name, rules)
26
+
27
+ if decision == Decision.ALLOW:
28
+ if command is not None:
29
+ # Shell command path: signal auto_approval to skip confirmation
30
+ logger.info(
31
+ "Policy auto-approved %s (command=%s, rule=%s)",
32
+ tool_name,
33
+ command,
34
+ matched_rule.description if matched_rule else "default",
35
+ )
36
+ return {"auto_approve": True}
37
+ # Non-shell tool path: auto_approve not relevant, just allow
38
+ logger.debug("Policy allowed %s (no confirmation to skip)", tool_name)
39
+ return None
40
+
41
+ if decision == Decision.DENY:
42
+ message = _build_block_message(matched_rule, tool_name)
43
+ logger.warning(
44
+ "Policy blocked %s (command=%s): %s", tool_name, command, message
45
+ )
46
+ return {"blocked": True, "error_message": message}
47
+
48
+ if decision == Decision.ASK_USER:
49
+ logger.debug("Policy ASK_USER for %s (command=%s)", tool_name, command)
50
+ return None
51
+
52
+ # Should never reach here, but default to normal flow
53
+ return None
54
+
55
+
56
+ def _build_block_message(rule: Any, tool_name: str) -> str:
57
+ if rule and getattr(rule, "description", None):
58
+ return f"🚫 Policy: {rule.description}"
59
+ return f"🚫 Policy: blocked {tool_name}"