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,410 @@
1
+ """Ollama cloud model setup β€” /ollama-setup command.
2
+
3
+ Pulls an Ollama `:cloud` model and registers it in extra_models.json so the
4
+ model is immediately available for use in Muse.
5
+
6
+ Cloud models supported (the Ollama "Recommended Models" cloud tier):
7
+ kimi-k2.6:cloud, kimi-k2.5:cloud, glm-5:cloud, glm-5.1:cloud, minimax-m2.7:cloud, qwen3.5:cloud
8
+ """
9
+
10
+ import json
11
+ import logging
12
+ import shutil
13
+ import subprocess
14
+ from pathlib import Path
15
+ from typing import Any
16
+
17
+ from code_muse.callbacks import register_callback
18
+ from code_muse.config import EXTRA_MODELS_FILE
19
+ from code_muse.messaging import emit_error, emit_info, emit_success, emit_warning
20
+
21
+ logger = logging.getLogger(__name__)
22
+
23
+ # ── Cloud model catalogue ───────────────────────────────────────────────────
24
+ # The `:cloud` models from the Ollama recommended list.
25
+ # Each entry maps the ollama tag β†’ extra_models.json config metadata.
26
+
27
+ CLOUD_MODELS: dict[str, dict[str, Any]] = {
28
+ "kimi-k2.6:cloud": {
29
+ "context_length": 262144, # 256k
30
+ "description": "Moonshot Kimi K2.6 (cloud)",
31
+ },
32
+ "kimi-k2.5:cloud": {
33
+ "context_length": 262144, # 256k
34
+ "description": "Moonshot Kimi K2.5 (cloud)",
35
+ },
36
+ "glm-5:cloud": {
37
+ "context_length": 204800, # 200k
38
+ "description": "ZhipuAI GLM-5 (cloud)",
39
+ },
40
+ "glm-5.1:cloud": {
41
+ "context_length": 204800, # 200k
42
+ "description": "ZhipuAI GLM-5.1 (cloud)",
43
+ },
44
+ "minimax-m2.7:cloud": {
45
+ "context_length": 199680, # 195k
46
+ "description": "MiniMax M2.7 (cloud)",
47
+ },
48
+ "qwen3.5:cloud": {
49
+ "context_length": 204800, # 200k
50
+ "description": "Alibaba Qwen 3.5 (cloud)",
51
+ },
52
+ }
53
+
54
+ OLLAMA_ENDPOINT = "http://localhost:11434/v1"
55
+ OLLAMA_API_KEY = "ollama" # Ollama's local API doesn't need a real key
56
+
57
+
58
+ # ── Helpers ─────────────────────────────────────────────────────────────────
59
+
60
+
61
+ def _model_key(model_tag: str) -> str:
62
+ """Derive a unique extra_models.json key from an ollama tag.
63
+
64
+ e.g. ``glm-5:cloud`` β†’ ``ollama-glm-5-cloud``
65
+ """
66
+ return "ollama-" + model_tag.replace(":", "-").replace("/", "-")
67
+
68
+
69
+ def _ollama_available() -> bool:
70
+ """Return True if the ``ollama`` CLI is on PATH."""
71
+ return shutil.which("ollama") is not None
72
+
73
+
74
+ def _pull_model(model_tag: str) -> bool:
75
+ """Run ``ollama pull <model_tag>``, streaming output to the terminal.
76
+
77
+ Returns True on success, False on failure.
78
+ """
79
+ emit_info(f"[Run] Pulling {model_tag} via ollama …")
80
+ try:
81
+ result = subprocess.run(
82
+ ["ollama", "pull", model_tag],
83
+ capture_output=True,
84
+ text=True,
85
+ timeout=600,
86
+ )
87
+ if result.returncode != 0:
88
+ stderr = result.stderr.strip()
89
+ emit_error(f"ollama pull failed (exit {result.returncode}): {stderr}")
90
+ return False
91
+
92
+ stdout = result.stdout.strip()
93
+ if stdout:
94
+ emit_info(stdout)
95
+ return True
96
+ except subprocess.TimeoutExpired:
97
+ emit_error("ollama pull timed out after 10 minutes")
98
+ return False
99
+ except Exception as exc:
100
+ emit_error(f"Failed to run ollama pull: {exc}")
101
+ return False
102
+
103
+
104
+ def _register_model(model_tag: str) -> bool:
105
+ """Write (or update) the model entry in extra_models.json.
106
+
107
+ Uses atomic-write via a temp file, same pattern as add_model_menu.py.
108
+ Returns True on success.
109
+ """
110
+ meta = CLOUD_MODELS[model_tag]
111
+ key = _model_key(model_tag)
112
+
113
+ extra_path = Path(EXTRA_MODELS_FILE)
114
+ extra_models: dict[str, Any] = {}
115
+
116
+ if extra_path.exists():
117
+ try:
118
+ with open(extra_path, encoding="utf-8") as fh:
119
+ extra_models = json.load(fh)
120
+ if not isinstance(extra_models, dict):
121
+ emit_error("extra_models.json must be a dict, not a list")
122
+ return False
123
+ except json.JSONDecodeError as exc:
124
+ emit_error(f"Corrupt extra_models.json: {exc}")
125
+ return False
126
+
127
+ if key in extra_models:
128
+ emit_info(f"Model {key} already registered β€” updating entry")
129
+
130
+ extra_models[key] = {
131
+ "type": "custom_openai",
132
+ "name": model_tag,
133
+ "custom_endpoint": {
134
+ "url": OLLAMA_ENDPOINT,
135
+ "api_key": OLLAMA_API_KEY,
136
+ },
137
+ "context_length": meta["context_length"],
138
+ "supported_settings": ["temperature", "top_p"],
139
+ }
140
+
141
+ extra_path.parent.mkdir(parents=True, exist_ok=True)
142
+ tmp = extra_path.with_suffix(".tmp")
143
+ try:
144
+ with open(tmp, "w", encoding="utf-8") as fh:
145
+ json.dump(extra_models, fh, indent=4, ensure_ascii=False)
146
+ tmp.replace(extra_path)
147
+ except Exception as exc:
148
+ emit_error(f"Failed to write extra_models.json: {exc}")
149
+ return False
150
+
151
+ emit_success(f"βœ… Registered {key} in extra_models.json")
152
+ return True
153
+
154
+
155
+ def _test_model_auth(model_tag: str) -> tuple[bool, str]:
156
+ """Test if the model requires authentication.
157
+
158
+ Returns (authorized: bool, message: str).
159
+ If unauthorized, message contains guidance for the user.
160
+ """
161
+ import json as json_mod
162
+ import urllib.request
163
+
164
+ test_payload = json_mod.dumps(
165
+ {
166
+ "model": model_tag,
167
+ "messages": [{"role": "user", "content": "hi"}],
168
+ "stream": False,
169
+ }
170
+ ).encode("utf-8")
171
+
172
+ req = urllib.request.Request(
173
+ f"{OLLAMA_ENDPOINT}/chat/completions",
174
+ data=test_payload,
175
+ headers={
176
+ "Content-Type": "application/json",
177
+ "Authorization": f"Bearer {OLLAMA_API_KEY}",
178
+ },
179
+ method="POST",
180
+ )
181
+
182
+ try:
183
+ with urllib.request.urlopen(req, timeout=10):
184
+ # 200 = good to go
185
+ return (True, "")
186
+ except urllib.error.HTTPError as e:
187
+ body = e.read().decode("utf-8", errors="replace")
188
+ # Check for auth errors
189
+ if e.code == 401 or "unauthorized" in body.lower():
190
+ return (False, _auth_help_message(model_tag))
191
+ if e.code == 500 and (
192
+ "internal service error" in body.lower() or "unauthorized" in body.lower()
193
+ ):
194
+ # Cloud models often return 500 instead of 401
195
+ return (False, _auth_help_message(model_tag))
196
+ return (False, f"HTTP {e.code}: {body[:200]}")
197
+ except Exception as e:
198
+ return (False, f"Connection test failed: {e}")
199
+
200
+
201
+ def _auth_help_message(model_tag: str) -> str:
202
+ """Generate a helpful auth guidance message."""
203
+ return f"""πŸ” Authentication Required for {model_tag}
204
+
205
+ This is a cloud-hosted model (756B+ parameters) that requires Ollama Cloud access.
206
+
207
+ To use this model:
208
+ 1. Run: ollama login
209
+ 2. Visit https://ollama.com to create an account
210
+ 3. Subscribe to Ollama Cloud (cloud models aren't free)
211
+ 4. Accept the terms for {model_tag.split(":")[0]}
212
+
213
+ After logging in, you can use the model with:
214
+ /model {_model_key(model_tag)}
215
+ """
216
+
217
+
218
+ def _run_ollama_login() -> bool:
219
+ """Run ``ollama login`` interactively in the user's terminal.
220
+
221
+ This will open a browser for OAuth flow. Returns True if login succeeded.
222
+ """
223
+ emit_info(
224
+ "πŸ” Launching 'ollama login' β€” this will open your browser for authentication..."
225
+ )
226
+ emit_info(" (Press Ctrl+C here if you want to cancel and do it manually later)")
227
+ emit_info("")
228
+
229
+ try:
230
+ # Run ollama login with stdout/stderr connected to terminal
231
+ # so the user can see the browser-open message and any instructions
232
+ result = subprocess.run(
233
+ ["ollama", "login"],
234
+ capture_output=False, # Let it flow to terminal
235
+ text=True,
236
+ timeout=120, # 2 minutes β€” OAuth can take a while
237
+ )
238
+
239
+ if result.returncode == 0:
240
+ emit_success("βœ… Login completed!")
241
+ return True
242
+ else:
243
+ emit_error(f"ollama login exited with code {result.returncode}")
244
+ return False
245
+ except subprocess.TimeoutExpired:
246
+ emit_error("ollama login timed out after 2 minutes")
247
+ return False
248
+ except Exception as exc:
249
+ emit_error(f"Failed to run ollama login: {exc}")
250
+ return False
251
+
252
+
253
+ def _start_ollama_serve() -> subprocess.Popen | None:
254
+ """Start ``ollama serve`` as a background subprocess if not already running.
255
+
256
+ Returns the Popen handle, or None if already running / on failure.
257
+ """
258
+ # Quick liveness check β€” hit the version endpoint
259
+ try:
260
+ result = subprocess.run(
261
+ ["ollama", "list"],
262
+ capture_output=True,
263
+ text=True,
264
+ timeout=5,
265
+ )
266
+ if result.returncode == 0:
267
+ emit_info("🟒 Ollama is already running")
268
+ return None
269
+ except Exception:
270
+ pass
271
+
272
+ emit_info("πŸš€ Starting ollama serve in the background …")
273
+ try:
274
+ proc = subprocess.Popen(
275
+ ["ollama", "serve"],
276
+ stdout=subprocess.DEVNULL,
277
+ stderr=subprocess.DEVNULL,
278
+ )
279
+ emit_success(f"ollama serve started (pid {proc.pid})")
280
+ return proc
281
+ except Exception as exc:
282
+ emit_error(f"Could not start ollama serve: {exc}")
283
+ return None
284
+
285
+
286
+ # ── Command handler ─────────────────────────────────────────────────────────
287
+
288
+
289
+ def _handle_ollama_setup(command: str, name: str) -> Any:
290
+ """Handle ``/ollama-setup [model]``."""
291
+ if name != "ollama-setup":
292
+ return None # Not our command
293
+
294
+ parts = command.split(maxsplit=1)
295
+ model_tag = parts[1].strip() if len(parts) > 1 else ""
296
+
297
+ # No argument β†’ show available models
298
+ if not model_tag:
299
+ lines = ["πŸ¦™ Available Ollama cloud models:\n"]
300
+ for tag, meta in CLOUD_MODELS.items():
301
+ lines.append(f" β€’ {tag:25s} β€” {meta['description']}")
302
+ lines.append("\nUsage: /ollama-setup <model>")
303
+ emit_info("\n".join(lines))
304
+ return True
305
+
306
+ # Fuzzy-ish match: allow partial names like "glm" β†’ "glm-5:cloud"
307
+ matched = _resolve_model(model_tag)
308
+ if matched is None:
309
+ emit_error(f"Unknown model '{model_tag}'. Available: {', '.join(CLOUD_MODELS)}")
310
+ return True
311
+
312
+ # Pre-flight: is ollama installed?
313
+ if not _ollama_available():
314
+ emit_error(
315
+ "ollama CLI not found on PATH. "
316
+ "Install it from https://ollama.com and try again."
317
+ )
318
+ return True
319
+
320
+ # 1. Ensure ollama serve is up
321
+ _start_ollama_serve()
322
+
323
+ # 2. Pull the model
324
+ if not _pull_model(matched):
325
+ return True
326
+
327
+ # 3. Register in extra_models.json
328
+ _register_model(matched)
329
+
330
+ # 4. Test auth / accessibility
331
+ emit_info(f"πŸ” Testing {matched} accessibility...")
332
+ authorized, msg = _test_model_auth(matched)
333
+
334
+ if authorized:
335
+ emit_success(
336
+ f"πŸŽ‰ Done! Switch to the model with: /model {_model_key(matched)}"
337
+ )
338
+ else:
339
+ emit_warning(msg)
340
+ emit_info("")
341
+ emit_info(
342
+ "πŸ’‘ Want me to run 'ollama login' for you? (This will open your browser)"
343
+ )
344
+ emit_info(
345
+ " Type 'yes' to proceed, or anything else to skip and do it manually later."
346
+ )
347
+
348
+ # We can't do interactive input here (custom commands shouldn't block)
349
+ # So we just run it β€” worst case it fails and they do it manually
350
+ login_ok = _run_ollama_login()
351
+
352
+ if login_ok:
353
+ # Re-test auth after login
354
+ emit_info("πŸ” Re-testing accessibility after login...")
355
+ authorized, msg = _test_model_auth(matched)
356
+
357
+ if authorized:
358
+ emit_success(
359
+ f"πŸŽ‰ All set! Switch to the model with: /model {_model_key(matched)}"
360
+ )
361
+ else:
362
+ emit_warning("Still not authorized after login.")
363
+ emit_info(
364
+ f"πŸ“‹ Model registered as '{_model_key(matched)}'. "
365
+ f"You may need to subscribe to Ollama Cloud at https://ollama.com, "
366
+ f"then use /model {_model_key(matched)}"
367
+ )
368
+ else:
369
+ emit_info(
370
+ f"πŸ“‹ Model registered as '{_model_key(matched)}' but needs auth. "
371
+ f"Run 'ollama login' manually, then use /model {_model_key(matched)}"
372
+ )
373
+
374
+ return True
375
+
376
+
377
+ def _resolve_model(user_input: str) -> str | None:
378
+ """Resolve user input to an exact cloud model tag.
379
+
380
+ Supports exact match and unambiguous prefix matching.
381
+ """
382
+ lowered = user_input.lower().strip()
383
+
384
+ # Exact match first
385
+ for tag in CLOUD_MODELS:
386
+ if tag.lower() == lowered:
387
+ return tag
388
+
389
+ # Prefix / substring match
390
+ candidates = [t for t in CLOUD_MODELS if lowered in t.lower()]
391
+ if len(candidates) == 1:
392
+ return candidates[0]
393
+
394
+ return None
395
+
396
+
397
+ # ── Help entry ──────────────────────────────────────────────────────────────
398
+
399
+
400
+ def _custom_help():
401
+ model_names = ", ".join(CLOUD_MODELS)
402
+ return [
403
+ ("ollama-setup", f"Pull & register an Ollama cloud model ({model_names})"),
404
+ ]
405
+
406
+
407
+ # ── Register ────────────────────────────────────────────────────────────────
408
+
409
+ register_callback("custom_command", _handle_ollama_setup)
410
+ register_callback("custom_command_help", _custom_help)
File without changes
@@ -0,0 +1,206 @@
1
+ """Plan Command Plugin β€” /plan slash command.
2
+
3
+ Invokes the planning-agent to break down a coding task into
4
+ clear, actionable steps and displays the plan to the user.
5
+ """
6
+
7
+ import logging
8
+ from contextlib import AsyncExitStack
9
+ from dataclasses import dataclass
10
+ from functools import partial
11
+
12
+ from code_muse.callbacks import on_agent_run_context, register_callback
13
+ from code_muse.messaging import emit_info, emit_success, emit_warning
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+
18
+ # ---------------------------------------------------------------------------
19
+ # Lightweight standalone agent invocation (mirrors invoke_agent tool logic)
20
+ # ---------------------------------------------------------------------------
21
+
22
+
23
+ @dataclass
24
+ class _AgentResult:
25
+ """Simple container for an invoked agent's response."""
26
+
27
+ response_text: str | None = None
28
+ error: str | None = None
29
+
30
+
31
+ async def _invoke_agent(agent_name: str, prompt: str) -> _AgentResult:
32
+ """Invoke a named agent with a prompt and return its text response.
33
+
34
+ This is a lightweight standalone wrapper that mirrors the core
35
+ ``invoke_agent`` tool logic but doesn't require a Pydantic-AI
36
+ ``RunContext`` β€” making it callable from plugin callbacks.
37
+ """
38
+ from pydantic_ai import Agent, UsageLimits
39
+
40
+ from code_muse.agents._compaction import make_history_processor
41
+ from code_muse.agents.agent_manager import load_agent
42
+ from code_muse.agents.subagent_stream_handler import subagent_stream_handler
43
+ from code_muse.config import get_message_limit
44
+ from code_muse.model_factory import ModelFactory, make_model_settings
45
+ from code_muse.model_utils import prepare_prompt_for_model
46
+ from code_muse.tools import register_tools_for_agent
47
+ from code_muse.tools.common import generate_group_id
48
+ from code_muse.tools.subagent_context import subagent_context
49
+
50
+ group_id = generate_group_id("invoke_agent", agent_name)
51
+
52
+ try:
53
+ agent_config = load_agent(agent_name)
54
+
55
+ model_name = agent_config.get_model_name()
56
+ models_config = ModelFactory.load_config()
57
+
58
+ if model_name not in models_config:
59
+ return _AgentResult(
60
+ error=f"Model '{model_name}' not found in configuration"
61
+ )
62
+
63
+ model = ModelFactory.get_model(model_name, models_config)
64
+
65
+ instructions = agent_config.get_full_system_prompt()
66
+
67
+ # Add AGENTS.md content
68
+ from code_muse.agents._builder import load_muse_rules
69
+
70
+ agent_rules = load_muse_rules()
71
+ if agent_rules:
72
+ instructions += f"\n\n{agent_rules}"
73
+
74
+ # Apply plugin prompt additions
75
+ from code_muse import callbacks as cb
76
+
77
+ prompt_additions = cb.on_load_prompt()
78
+ if prompt_additions:
79
+ instructions += "\n" + "\n".join(prompt_additions)
80
+
81
+ # Prepare prompt for model (handles claude-code models etc.)
82
+ prepared = prepare_prompt_for_model(
83
+ model_name,
84
+ instructions,
85
+ prompt,
86
+ prepend_system_to_user=True,
87
+ )
88
+ instructions = prepared.instructions
89
+ prompt = prepared.user_prompt
90
+
91
+ model_settings = make_model_settings(model_name)
92
+
93
+ temp_agent = Agent(
94
+ model=model,
95
+ instructions=instructions,
96
+ output_type=str,
97
+ retries=3,
98
+ toolsets=[],
99
+ history_processors=[make_history_processor(agent_config)],
100
+ model_settings=model_settings,
101
+ )
102
+
103
+ # Register the tools the agent needs
104
+ agent_tools = agent_config.get_available_tools()
105
+ register_tools_for_agent(temp_agent, agent_tools, model_name=model_name)
106
+
107
+ # Use subagent_stream_handler for clean output
108
+ stream_handler = partial(
109
+ subagent_stream_handler, session_id=f"{agent_name}-plan-session"
110
+ )
111
+
112
+ with subagent_context(agent_name):
113
+ run_ctxs = on_agent_run_context(agent_config, temp_agent, group_id)
114
+ async with AsyncExitStack() as stack:
115
+ for cm in run_ctxs:
116
+ await stack.enter_async_context(cm)
117
+ result = await temp_agent.run(
118
+ prompt,
119
+ message_history=[],
120
+ usage_limits=UsageLimits(request_limit=get_message_limit()),
121
+ event_stream_handler=stream_handler,
122
+ )
123
+
124
+ return _AgentResult(response_text=result.output)
125
+
126
+ except Exception as exc:
127
+ logger.exception("invoke_agent failed for %s", agent_name)
128
+ return _AgentResult(error=str(exc))
129
+
130
+
131
+ # ---------------------------------------------------------------------------
132
+ # Slash-command help
133
+ # ---------------------------------------------------------------------------
134
+
135
+
136
+ def _custom_help() -> list[tuple[str, str]]:
137
+ """Provide help entry for /help display."""
138
+ return [
139
+ ("plan", "Invoke the Planning Agent to create an execution roadmap"),
140
+ ]
141
+
142
+
143
+ # ---------------------------------------------------------------------------
144
+ # Slash-command handler
145
+ # ---------------------------------------------------------------------------
146
+
147
+
148
+ async def _handle_custom_command(command: str, name: str) -> bool | None:
149
+ """Handle the /plan slash command.
150
+
151
+ Usage:
152
+ /plan <description of what to plan>
153
+
154
+ Invokes the planning-agent with the user's request and displays
155
+ the resulting plan.
156
+
157
+ Returns:
158
+ True if handled.
159
+ None if the command name doesn't match.
160
+ """
161
+ if name != "plan":
162
+ return None
163
+
164
+ # Extract the planning request (everything after "/plan")
165
+ parts = command.split(maxsplit=1)
166
+ plan_request = parts[1].strip() if len(parts) > 1 else ""
167
+
168
+ if not plan_request:
169
+ emit_warning(
170
+ "Usage: /plan <description of what to plan>\n"
171
+ "Example: /plan Add user authentication with OAuth2"
172
+ )
173
+ return True
174
+
175
+ emit_info("πŸ“‹ Planning Agent is creating your roadmap…")
176
+
177
+ result = await _invoke_agent(
178
+ agent_name="planning-agent",
179
+ prompt=(
180
+ f"Create a detailed execution plan for the following task:\n\n"
181
+ f"{plan_request}\n\n"
182
+ f"Include: project structure analysis, dependencies, "
183
+ f"execution steps, and validation strategy."
184
+ ),
185
+ )
186
+
187
+ # Display the plan
188
+ if result.response_text:
189
+ emit_success("πŸ“‹ Planning Agent β€” Execution Roadmap")
190
+ emit_info(result.response_text)
191
+ elif result.error:
192
+ emit_warning(f"Planning Agent error: {result.error}")
193
+ else:
194
+ emit_warning("Planning Agent returned no output.")
195
+
196
+ return True # Handled β€” no additional model invocation needed
197
+
198
+
199
+ # ---------------------------------------------------------------------------
200
+ # Register callbacks
201
+ # ---------------------------------------------------------------------------
202
+
203
+ register_callback("custom_command_help", _custom_help)
204
+ register_callback("custom_command", _handle_custom_command)
205
+
206
+ logger.debug("Plan Command plugin callbacks registered")
@@ -0,0 +1,37 @@
1
+ """Plan Mode plugin for Muse.
2
+
3
+ Provides tools and hooks to enter/exit a planning-only mode where
4
+ destructive tools (write, replace, delete, shell) are blocked while
5
+ research tools (read, list, grep, ask) remain available.
6
+ """
7
+
8
+ from code_muse.plugins.plan_mode.mode_cycling import cycle_mode
9
+ from code_muse.plugins.plan_mode.plan_generation import generate_plan_md, save_plan
10
+ from code_muse.plugins.plan_mode.plan_hooks import plan_mode_pre_tool_call_hook
11
+ from code_muse.plugins.plan_mode.plan_mode_tools import (
12
+ PlanModeState,
13
+ get_current_mode,
14
+ get_plan_goal,
15
+ register_approve_plan,
16
+ register_enter_plan_mode,
17
+ register_exit_plan_mode,
18
+ register_get_plan_mode,
19
+ register_open_plan_in_editor,
20
+ set_plan_mode,
21
+ )
22
+
23
+ __all__ = [
24
+ "PlanModeState",
25
+ "get_current_mode",
26
+ "get_plan_goal",
27
+ "set_plan_mode",
28
+ "register_enter_plan_mode",
29
+ "register_exit_plan_mode",
30
+ "register_get_plan_mode",
31
+ "register_approve_plan",
32
+ "register_open_plan_in_editor",
33
+ "generate_plan_md",
34
+ "save_plan",
35
+ "cycle_mode",
36
+ "plan_mode_pre_tool_call_hook",
37
+ ]
@@ -0,0 +1,40 @@
1
+ """Mode cycling logic and UI emission.
2
+
3
+ Provides a ``cycle_mode()`` function that rotates through the three
4
+ plan-mode states. A ``/mode`` slash command exposes this in the UI.
5
+
6
+ TODO: Shift+Tab key-binding integration requires future prompt_toolkit
7
+ hooking (not implemented here to avoid core changes).
8
+ """
9
+
10
+ from code_muse.messaging import emit_info
11
+ from code_muse.plugins.plan_mode.plan_mode_tools import (
12
+ PlanModeState,
13
+ get_current_mode,
14
+ set_plan_mode,
15
+ )
16
+
17
+ # Cyclic ordering: DEFAULT β†’ AUTO_EDIT β†’ PLAN β†’ DEFAULT
18
+ _MODE_CYCLE = [
19
+ PlanModeState.DEFAULT,
20
+ PlanModeState.AUTO_EDIT,
21
+ PlanModeState.PLAN,
22
+ ]
23
+
24
+
25
+ def cycle_mode() -> PlanModeState:
26
+ """Advance to the next mode in the cycle and emit a UI indicator.
27
+
28
+ Returns:
29
+ The new active mode.
30
+ """
31
+ current = get_current_mode()
32
+ try:
33
+ idx = _MODE_CYCLE.index(current)
34
+ except ValueError:
35
+ idx = 0
36
+ next_idx = (idx + 1) % len(_MODE_CYCLE)
37
+ new_mode = _MODE_CYCLE[next_idx]
38
+ set_plan_mode(new_mode)
39
+ emit_info(f"πŸ” Mode cycled to: {new_mode.value}")
40
+ return new_mode