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,740 @@
1
+ """Command handlers for Muse - CORE commands.
2
+
3
+ This module contains @register_command decorated handlers that are automatically
4
+ discovered by the command registry system.
5
+ """
6
+
7
+ import os
8
+ from pathlib import Path
9
+
10
+ from code_muse.command_line.agent_menu import interactive_agent_picker
11
+ from code_muse.command_line.command_registry import register_command
12
+ from code_muse.command_line.model_picker_completion import (
13
+ interactive_model_picker,
14
+ update_model_in_input,
15
+ )
16
+ from code_muse.command_line.utils import make_directory_table
17
+ from code_muse.config import finalize_autosave_session
18
+ from code_muse.messaging import emit_error, emit_info
19
+ from code_muse.tools.tools_content import tools_content
20
+
21
+
22
+ # Import get_commands_help from command_handler to avoid circular imports
23
+ # This will be defined in command_handler.py
24
+ def get_commands_help():
25
+ """Lazy import to avoid circular dependency."""
26
+ from code_muse.command_line.command_handler import get_commands_help as _gch
27
+
28
+ return _gch()
29
+
30
+
31
+ @register_command(
32
+ name="help",
33
+ description="Show this help message",
34
+ usage="/help, /h",
35
+ aliases=["h"],
36
+ category="core",
37
+ )
38
+ def handle_help_command(command: str) -> bool:
39
+ """Show commands help."""
40
+ import uuid
41
+
42
+ from code_muse.messaging import emit_info
43
+
44
+ group_id = str(uuid.uuid4())
45
+ help_text = get_commands_help()
46
+ emit_info(help_text, message_group_id=group_id)
47
+ return True
48
+
49
+
50
+ @register_command(
51
+ name="cd",
52
+ description="Change directory or show directories",
53
+ usage="/cd <dir>",
54
+ category="core",
55
+ )
56
+ def handle_cd_command(command: str) -> bool:
57
+ """Change directory or list current directory."""
58
+ import shlex
59
+
60
+ from code_muse.messaging import emit_error, emit_info, emit_success, emit_warning
61
+
62
+ try:
63
+ if os.name == "nt":
64
+ # Windows paths commonly use backslashes; POSIX shlex treats them as
65
+ # escape characters and corrupts valid paths (e.g., C:\foo\bar).
66
+ lexer = shlex.shlex(command, posix=False)
67
+ lexer.whitespace_split = True
68
+ lexer.commenters = ""
69
+ tokens = list(lexer)
70
+ else:
71
+ tokens = shlex.split(command)
72
+ except ValueError:
73
+ # Keep remaining text as one argument for better resilience.
74
+ tokens = command.split(maxsplit=1)
75
+
76
+ if len(tokens) == 1:
77
+ try:
78
+ table = make_directory_table()
79
+ emit_info(table)
80
+ except Exception as e:
81
+ emit_error(f"Error listing directory: {e}")
82
+ return True
83
+
84
+ if len(tokens) >= 2:
85
+ # /cd takes one path argument; if tokenizer split extra whitespace,
86
+ # rejoin it so unquoted paths with spaces still have a chance.
87
+ dirname = " ".join(tokens[1:]).strip().strip("\"'")
88
+ target = Path(dirname).expanduser()
89
+ if not target.is_absolute():
90
+ target = Path.cwd() / target
91
+ if target.is_dir():
92
+ os.chdir(target)
93
+ emit_success(f"Changed directory to: {target}")
94
+ # Reload the agent so the system prompt and project-local
95
+ # AGENT.md rules reflect the new working directory. Without
96
+ # this, the LLM keeps receiving stale path information for the
97
+ # remainder of the session (the PydanticAgent instructions are
98
+ # baked in at construction time and never refreshed otherwise).
99
+ try:
100
+ from code_muse.agents.agent_manager import get_current_agent
101
+
102
+ get_current_agent().reload_code_generation_agent()
103
+ except Exception as e:
104
+ emit_warning(
105
+ f"Directory changed, but agent reload failed: {e}. "
106
+ "You may need to run /agent or /model to force a refresh."
107
+ )
108
+ else:
109
+ emit_error(f"Not a directory: {dirname}")
110
+ return True
111
+
112
+ return True
113
+
114
+
115
+ @register_command(
116
+ name="tools",
117
+ description="Show available tools and capabilities",
118
+ usage="/tools",
119
+ category="core",
120
+ )
121
+ def handle_tools_command(command: str) -> bool:
122
+ """Display available tools."""
123
+ from rich.markdown import Markdown
124
+
125
+ from code_muse.messaging import emit_info
126
+
127
+ markdown_content = Markdown(tools_content)
128
+ emit_info(markdown_content)
129
+ return True
130
+
131
+
132
+ @register_command(
133
+ name="paste",
134
+ description="Paste image from clipboard (same as F3, or Ctrl+V with image)",
135
+ usage="/paste, /clipboard, /cb",
136
+ aliases=["clipboard", "cb"],
137
+ category="core",
138
+ )
139
+ def handle_paste_command(command: str) -> bool:
140
+ """Paste an image from the clipboard into the pending attachments."""
141
+ from code_muse.command_line.clipboard import (
142
+ capture_clipboard_image_to_pending,
143
+ get_clipboard_manager,
144
+ has_image_in_clipboard,
145
+ )
146
+ from code_muse.messaging import emit_info, emit_success, emit_warning
147
+
148
+ if not has_image_in_clipboard():
149
+ emit_warning("No image found in clipboard")
150
+ emit_info("Copy an image (screenshot, from browser, etc.) and try again")
151
+ return True
152
+
153
+ placeholder = capture_clipboard_image_to_pending()
154
+ if placeholder:
155
+ manager = get_clipboard_manager()
156
+ count = manager.get_pending_count()
157
+ emit_success(f"📋 {placeholder}")
158
+ emit_info(f"Total pending clipboard images: {count}")
159
+ emit_info("Type your prompt and press Enter to send with the image(s)")
160
+ else:
161
+ emit_warning("Failed to capture clipboard image")
162
+
163
+ return True
164
+
165
+
166
+ @register_command(
167
+ name="tutorial",
168
+ description="Run the interactive tutorial wizard",
169
+ usage="/tutorial",
170
+ category="core",
171
+ )
172
+ def handle_tutorial_command(command: str) -> bool:
173
+ """Run the interactive tutorial wizard.
174
+
175
+ Usage:
176
+ /tutorial - Run the tutorial (can be run anytime)
177
+ """
178
+ import asyncio
179
+ import concurrent.futures
180
+
181
+ from code_muse.command_line.onboarding_wizard import (
182
+ reset_onboarding,
183
+ run_onboarding_wizard,
184
+ )
185
+ from code_muse.model_switching import set_model_and_reload_agent
186
+
187
+ # Always reset so user can re-run the tutorial anytime
188
+ reset_onboarding()
189
+
190
+ # Run the async wizard in a thread pool (same pattern as agent picker).
191
+ # FREE-THREADED: ThreadPoolExecutor is compatible with free-threaded Python 3.14.
192
+ with concurrent.futures.ThreadPoolExecutor() as executor:
193
+ future = executor.submit(lambda: asyncio.run(run_onboarding_wizard()))
194
+ result = future.result(timeout=300) # 5 min timeout
195
+
196
+ if result == "chatgpt":
197
+ emit_info("🔐 Starting ChatGPT OAuth flow...")
198
+ from code_muse.plugins.chatgpt_oauth.oauth_flow import run_oauth_flow
199
+
200
+ run_oauth_flow()
201
+ set_model_and_reload_agent("chatgpt-gpt-5.4")
202
+ elif result == "claude":
203
+ emit_info("🔐 Starting Claude Code OAuth flow...")
204
+ from code_muse.plugins.claude_code_oauth.register_callbacks import (
205
+ _perform_authentication,
206
+ )
207
+
208
+ _perform_authentication()
209
+ set_model_and_reload_agent("claude-code-claude-opus-4-7")
210
+ elif result == "completed":
211
+ emit_info("🎉 Tutorial complete! Happy coding!")
212
+ elif result == "skipped":
213
+ emit_info("⏭️ Tutorial skipped. Run /tutorial anytime!")
214
+
215
+ return True
216
+
217
+
218
+ @register_command(
219
+ name="exit",
220
+ description="Exit interactive mode",
221
+ usage="/exit, /quit",
222
+ aliases=["quit"],
223
+ category="core",
224
+ )
225
+ def handle_exit_command(command: str) -> bool:
226
+ """Exit the interactive session."""
227
+ from code_muse.messaging import emit_success
228
+
229
+ try:
230
+ emit_success("Goodbye!")
231
+ except Exception:
232
+ # Handle emit errors gracefully
233
+ pass
234
+ # Signal to the main app that we want to exit
235
+ # The actual exit handling is done in main.py
236
+ return True
237
+
238
+
239
+ @register_command(
240
+ name="agent",
241
+ description="Switch to a different agent or show available agents",
242
+ usage="/agent <name>, /a <name>",
243
+ aliases=["a"],
244
+ category="core",
245
+ )
246
+ def handle_agent_command(command: str) -> bool:
247
+ """Handle agent switching."""
248
+ from rich.text import Text
249
+
250
+ from code_muse.agents import (
251
+ get_agent_descriptions,
252
+ get_available_agents,
253
+ get_current_agent,
254
+ set_current_agent,
255
+ )
256
+ from code_muse.messaging import emit_error, emit_info, emit_success, emit_warning
257
+
258
+ tokens = command.split()
259
+
260
+ if len(tokens) == 1:
261
+ # Show interactive agent picker
262
+ try:
263
+ # Run the async picker using asyncio utilities
264
+ # Since we're called from an async context but this function is sync,
265
+ # we need to carefully schedule and wait for the coroutine
266
+ import asyncio
267
+ import concurrent.futures
268
+ import uuid
269
+
270
+ # Create a new event loop in a thread and run the picker there.
271
+ # FREE-THREADED: ThreadPoolExecutor is compatible with free-threaded Python 3.14.
272
+ with concurrent.futures.ThreadPoolExecutor() as executor:
273
+ future = executor.submit(
274
+ lambda: asyncio.run(interactive_agent_picker())
275
+ )
276
+ selected_agent = future.result(timeout=300) # 5 min timeout
277
+
278
+ if selected_agent:
279
+ current_agent = get_current_agent()
280
+ # Check if we're already using this agent
281
+ if current_agent.name == selected_agent:
282
+ group_id = str(uuid.uuid4())
283
+ emit_info(
284
+ f"Already using agent: {current_agent.display_name}",
285
+ message_group=group_id,
286
+ )
287
+ return True
288
+
289
+ # Switch to the new agent
290
+ group_id = str(uuid.uuid4())
291
+ new_session_id = finalize_autosave_session()
292
+ if not set_current_agent(selected_agent):
293
+ emit_warning(
294
+ "Agent switch failed after autosave rotation. Your context was preserved.",
295
+ message_group=group_id,
296
+ )
297
+ return True
298
+
299
+ new_agent = get_current_agent()
300
+ new_agent.reload_code_generation_agent()
301
+ emit_success(
302
+ f"Switched to agent: {new_agent.display_name}",
303
+ message_group=group_id,
304
+ )
305
+ emit_info(f"{new_agent.description}", message_group=group_id)
306
+ emit_info(
307
+ Text.from_markup(
308
+ f"[dim]Auto-save session rotated to: {new_session_id}[/dim]"
309
+ ),
310
+ message_group=group_id,
311
+ )
312
+ else:
313
+ emit_warning("Agent selection cancelled")
314
+ return True
315
+ except Exception as e:
316
+ # Fallback to old behavior if picker fails
317
+ import traceback
318
+ import uuid
319
+
320
+ emit_warning(f"Interactive picker failed: {e}")
321
+ emit_warning(f"Traceback: {traceback.format_exc()}")
322
+
323
+ # Show current agent and available agents
324
+ current_agent = get_current_agent()
325
+ available_agents = get_available_agents()
326
+ descriptions = get_agent_descriptions()
327
+
328
+ # Generate a group ID for all messages in this command
329
+ group_id = str(uuid.uuid4())
330
+
331
+ emit_info(
332
+ Text.from_markup(
333
+ f"[bold green]Current Agent:[/bold green] {current_agent.display_name}"
334
+ ),
335
+ message_group=group_id,
336
+ )
337
+ emit_info(
338
+ Text.from_markup(f"[dim]{current_agent.description}[/dim]\n"),
339
+ message_group=group_id,
340
+ )
341
+
342
+ emit_info(
343
+ Text.from_markup("[bold magenta]Available Agents:[/bold magenta]"),
344
+ message_group=group_id,
345
+ )
346
+ for name, display_name in available_agents.items():
347
+ description = descriptions.get(name, "No description")
348
+ current_marker = (
349
+ " [green]← current[/green]" if name == current_agent.name else ""
350
+ )
351
+ emit_info(
352
+ Text.from_markup(
353
+ f" [cyan]{name:<12}[/cyan] {display_name}{current_marker}"
354
+ ),
355
+ message_group=group_id,
356
+ )
357
+ emit_info(f" {description}", message_group=group_id)
358
+
359
+ emit_info(
360
+ Text.from_markup("\n[yellow]Usage:[/yellow] /agent <agent-name>"),
361
+ message_group=group_id,
362
+ )
363
+ return True
364
+
365
+ elif len(tokens) == 2:
366
+ agent_name = tokens[1].lower()
367
+
368
+ # Generate a group ID for all messages in this command
369
+ import uuid
370
+
371
+ group_id = str(uuid.uuid4())
372
+ available_agents = get_available_agents()
373
+
374
+ if agent_name not in available_agents:
375
+ emit_error(f"Agent '{agent_name}' not found", message_group=group_id)
376
+ emit_warning(
377
+ f"Available agents: {', '.join(available_agents.keys())}",
378
+ message_group=group_id,
379
+ )
380
+ return True
381
+
382
+ current_agent = get_current_agent()
383
+ if current_agent.name == agent_name:
384
+ emit_info(
385
+ f"Already using agent: {current_agent.display_name}",
386
+ message_group=group_id,
387
+ )
388
+ return True
389
+
390
+ new_session_id = finalize_autosave_session()
391
+ if not set_current_agent(agent_name):
392
+ emit_warning(
393
+ "Agent switch failed after autosave rotation. Your context was preserved.",
394
+ message_group=group_id,
395
+ )
396
+ return True
397
+
398
+ new_agent = get_current_agent()
399
+ new_agent.reload_code_generation_agent()
400
+ emit_success(
401
+ f"Switched to agent: {new_agent.display_name}",
402
+ message_group=group_id,
403
+ )
404
+ emit_info(f"{new_agent.description}", message_group=group_id)
405
+ emit_info(
406
+ Text.from_markup(
407
+ f"[dim]Auto-save session rotated to: {new_session_id}[/dim]"
408
+ ),
409
+ message_group=group_id,
410
+ )
411
+ return True
412
+ else:
413
+ emit_warning("Usage: /agent [agent-name]")
414
+ return True
415
+
416
+
417
+ @register_command(
418
+ name="model",
419
+ description="Set active model",
420
+ usage="/model, /m <model>",
421
+ aliases=["m"],
422
+ category="core",
423
+ )
424
+ def handle_model_command(command: str) -> bool:
425
+ """Set the active model."""
426
+ import asyncio
427
+
428
+ from code_muse.command_line.model_picker_completion import (
429
+ get_active_model,
430
+ load_model_names,
431
+ set_active_model,
432
+ )
433
+ from code_muse.messaging import emit_success, emit_warning
434
+
435
+ tokens = command.split()
436
+
437
+ # If just /model or /m with no args, show interactive picker
438
+ if len(tokens) == 1:
439
+ try:
440
+ # Run the async picker using asyncio utilities
441
+ # Since we're called from an async context but this function is sync,
442
+ # we need to carefully schedule and wait for the coroutine
443
+ import concurrent.futures
444
+
445
+ # Create a new event loop in a thread and run the picker there.
446
+ # FREE-THREADED: ThreadPoolExecutor is compatible with free-threaded Python 3.14.
447
+ with concurrent.futures.ThreadPoolExecutor() as executor:
448
+ future = executor.submit(
449
+ lambda: asyncio.run(interactive_model_picker())
450
+ )
451
+ selected_model = future.result(timeout=300) # 5 min timeout
452
+
453
+ if selected_model:
454
+ set_active_model(selected_model)
455
+ emit_success(f"Active model set and loaded: {selected_model}")
456
+ else:
457
+ emit_warning("Model selection cancelled")
458
+ return True
459
+ except Exception as e:
460
+ # Fallback to old behavior if picker fails
461
+ import traceback
462
+
463
+ emit_warning(f"Interactive picker failed: {e}")
464
+ emit_warning(f"Traceback: {traceback.format_exc()}")
465
+ model_names = load_model_names()
466
+ emit_warning("Usage: /model <model-name> or /m <model-name>")
467
+ emit_warning(f"Available models: {', '.join(model_names)}")
468
+ return True
469
+
470
+ # Handle both /model and /m for backward compatibility
471
+ model_command = command
472
+ if command.startswith("/model"):
473
+ # Convert /model to /m for internal processing
474
+ model_command = command.replace("/model", "/m", 1)
475
+
476
+ # If model matched, set it
477
+ new_input = update_model_in_input(model_command)
478
+ if new_input is not None:
479
+ model = get_active_model()
480
+ emit_success(f"Active model set and loaded: {model}")
481
+ return True
482
+
483
+ # If no model matched, show error
484
+ model_names = load_model_names()
485
+ emit_warning("Usage: /model <model-name> or /m <model-name>")
486
+ emit_warning(f"Available models: {', '.join(model_names)}")
487
+ return True
488
+
489
+
490
+ @register_command(
491
+ name="add_model",
492
+ description="Browse and add models from models.dev catalog",
493
+ usage="/add_model",
494
+ category="core",
495
+ )
496
+ def handle_add_model_command(command: str) -> bool:
497
+ """Launch interactive model browser TUI."""
498
+ from code_muse.command_line.add_model_menu import interactive_model_picker
499
+ from code_muse.tools.command_runner import set_awaiting_user_input
500
+
501
+ set_awaiting_user_input(True)
502
+ try:
503
+ # interactive_model_picker is now synchronous - no async complications!
504
+ result = interactive_model_picker()
505
+
506
+ if result:
507
+ emit_info("Successfully added model configuration")
508
+ return True
509
+ except KeyboardInterrupt:
510
+ # User cancelled - this is expected behavior
511
+ return True
512
+ except Exception as e:
513
+ emit_error(f"Failed to launch model browser: {e}")
514
+ return False
515
+ finally:
516
+ set_awaiting_user_input(False)
517
+
518
+
519
+ @register_command(
520
+ name="remove_model",
521
+ description="Remove a model from extra_models.json",
522
+ usage="/remove_model <model_name>",
523
+ category="config",
524
+ )
525
+ def handle_remove_model_command(command: str) -> bool:
526
+ """Remove a model from extra_models.json by key."""
527
+ import json
528
+ import pathlib
529
+
530
+ from code_muse.config import EXTRA_MODELS_FILE, clear_model_cache
531
+ from code_muse.messaging import emit_success, emit_warning
532
+
533
+ tokens = command.split()
534
+ if len(tokens) < 2:
535
+ emit_warning("Usage: /remove_model <model_name>")
536
+ return False
537
+
538
+ model_name = tokens[1]
539
+ extra_models_path = pathlib.Path(EXTRA_MODELS_FILE)
540
+ extra_models: dict = {}
541
+
542
+ if extra_models_path.exists():
543
+ try:
544
+ with open(extra_models_path, encoding="utf-8") as f:
545
+ extra_models = json.load(f)
546
+ if not isinstance(extra_models, dict):
547
+ emit_warning("extra_models.json must be a dictionary")
548
+ return False
549
+ except json.JSONDecodeError as e:
550
+ emit_warning(f"Error parsing extra_models.json: {e}")
551
+ return False
552
+
553
+ if model_name not in extra_models:
554
+ available = list(extra_models.keys())
555
+ if available:
556
+ emit_warning(
557
+ f"Model '{model_name}' not found in extra_models.json. "
558
+ f"Available extra models: {', '.join(available)}"
559
+ )
560
+ else:
561
+ emit_warning(f"Model '{model_name}' not found. extra_models.json is empty.")
562
+ return False
563
+
564
+ del extra_models[model_name]
565
+
566
+ # Atomic write
567
+ extra_models_path.parent.mkdir(parents=True, exist_ok=True)
568
+ temp_path = extra_models_path.with_suffix(".tmp")
569
+ with open(temp_path, "w", encoding="utf-8") as f:
570
+ json.dump(extra_models, f, indent=4, ensure_ascii=False)
571
+ temp_path.replace(extra_models_path)
572
+
573
+ clear_model_cache()
574
+ emit_success(f"Removed '{model_name}' from extra_models.json")
575
+ return True
576
+
577
+
578
+ @register_command(
579
+ name="model_settings",
580
+ description="Configure per-model settings (temperature, seed, etc.)",
581
+ usage="/model_settings [--show [model_name]]",
582
+ aliases=["ms"],
583
+ category="config",
584
+ )
585
+ def handle_model_settings_command(command: str) -> bool:
586
+ """Launch interactive model settings TUI.
587
+
588
+ Opens a TUI showing all available models. Select a model to configure
589
+ its settings (temperature, seed, etc.). ESC closes the TUI.
590
+
591
+ Use --show [model_name] to display current settings without the TUI.
592
+ """
593
+ from code_muse.command_line.model_settings_menu import (
594
+ interactive_model_settings,
595
+ show_model_settings_summary,
596
+ )
597
+ from code_muse.messaging import emit_error, emit_info, emit_success, emit_warning
598
+ from code_muse.tools.command_runner import set_awaiting_user_input
599
+
600
+ tokens = command.split()
601
+
602
+ # Check for --show flag to just display current settings
603
+ if "--show" in tokens:
604
+ model_name = None
605
+ for t in tokens[1:]:
606
+ if not t.startswith("--"):
607
+ model_name = t
608
+ break
609
+ show_model_settings_summary(model_name)
610
+ return True
611
+
612
+ set_awaiting_user_input(True)
613
+ try:
614
+ result = interactive_model_settings()
615
+
616
+ if result:
617
+ emit_success("Model settings updated successfully")
618
+
619
+ # Always reload the active agent so settings take effect
620
+ from code_muse.agents import get_current_agent
621
+
622
+ try:
623
+ current_agent = get_current_agent()
624
+ current_agent.reload_code_generation_agent()
625
+ emit_info("Active agent reloaded")
626
+ except Exception as reload_error:
627
+ emit_warning(f"Agent reload failed: {reload_error}")
628
+
629
+ return True
630
+ except KeyboardInterrupt:
631
+ return True
632
+ except Exception as e:
633
+ emit_error(f"Failed to launch model settings: {e}")
634
+ return False
635
+ finally:
636
+ set_awaiting_user_input(False)
637
+
638
+
639
+ @register_command(
640
+ name="generate-pr-description",
641
+ description="Generate comprehensive PR description",
642
+ usage="/generate-pr-description [@dir]",
643
+ category="core",
644
+ )
645
+ def handle_generate_pr_description_command(command: str) -> str:
646
+ """Generate a PR description."""
647
+ # Parse directory argument (e.g., /generate-pr-description @some/dir)
648
+ tokens = command.split()
649
+ directory_context = ""
650
+ for t in tokens:
651
+ if t.startswith("@"):
652
+ directory_context = f" Please work in the directory: {t[1:]}"
653
+ break
654
+
655
+ # Hard-coded prompt from user requirements
656
+ pr_prompt = f"""Generate a comprehensive PR description for my current branch changes. Follow these steps:
657
+
658
+ 1 Discover the changes: Use git CLI to find the base branch (usually main/master/develop) and get the list of changed files, commits, and diffs.
659
+ 2 Analyze the code: Read and analyze all modified files to understand:
660
+ • What functionality was added/changed/removed
661
+ • The technical approach and implementation details
662
+ • Any architectural or design pattern changes
663
+ • Dependencies added/removed/updated
664
+ 3 Generate a structured PR description with these sections:
665
+ • Title: Concise, descriptive title (50 chars max)
666
+ • Summary: Brief overview of what this PR accomplishes
667
+ • Changes Made: Detailed bullet points of specific changes
668
+ • Technical Details: Implementation approach, design decisions, patterns used
669
+ • Files Modified: List of key files with brief description of changes
670
+ • Testing: What was tested and how (if applicable)
671
+ • Breaking Changes: Any breaking changes (if applicable)
672
+ • Additional Notes: Any other relevant information
673
+ 4 Create a markdown file: Generate a PR_DESCRIPTION.md file with proper GitHub markdown formatting that I can directly copy-paste into GitHub's PR
674
+ description field. Use proper markdown syntax with headers, bullet points, code blocks, and formatting.
675
+ 5 Make it review-ready: Ensure the description helps reviewers understand the context, approach, and impact of the changes.
676
+ 6. If you have Github CLI integration, or gh cli is installed and authenticated then find the PR for the branch we analyzed and update the PR description there and then delete the PR_DESCRIPTION.md file. (If you have a better name (title) for the PR, go ahead and update the title too.{directory_context}"""
677
+
678
+ # Return the prompt to be processed by the main chat system
679
+ return pr_prompt
680
+
681
+
682
+ @register_command(
683
+ name="wiggum",
684
+ description="Loop mode: re-run the same prompt when agent finishes (like Wiggum chasing donuts 🍩)",
685
+ usage="/wiggum <prompt>",
686
+ category="core",
687
+ )
688
+ def handle_wiggum_command(command: str) -> str | bool:
689
+ """Start wiggum loop mode.
690
+
691
+ When active, the agent will automatically re-run the same prompt
692
+ after completing, resetting context each time. Use Ctrl+C to stop.
693
+
694
+ Example:
695
+ /wiggum say hello world
696
+ """
697
+ from code_muse.command_line.wiggum_state import start_wiggum
698
+ from code_muse.messaging import emit_info, emit_success, emit_warning
699
+
700
+ # Extract the prompt after /wiggum
701
+ parts = command.split(maxsplit=1)
702
+ if len(parts) < 2 or not parts[1].strip():
703
+ emit_warning("Usage: /wiggum <prompt>")
704
+ emit_info("Example: /wiggum say hello world")
705
+ emit_info("This will repeatedly run 'say hello world' after each completion.")
706
+ emit_info("Press Ctrl+C to stop the loop.")
707
+ return True
708
+
709
+ prompt = parts[1].strip()
710
+
711
+ # Start wiggum mode
712
+ start_wiggum(prompt)
713
+ emit_success("🍩 WIGGUM MODE ACTIVATED!")
714
+ emit_info(f"Prompt: {prompt}")
715
+ emit_info("The agent will re-loop this prompt after each completion.")
716
+ emit_info("Press Ctrl+C to stop the wiggum loop.")
717
+
718
+ # Return the prompt to execute immediately
719
+ return prompt
720
+
721
+
722
+ @register_command(
723
+ name="wiggum_stop",
724
+ description="Stop wiggum loop mode",
725
+ usage="/wiggum_stop",
726
+ aliases=["stopwiggum", "ws"],
727
+ category="core",
728
+ )
729
+ def handle_wiggum_stop_command(command: str) -> bool:
730
+ """Stop wiggum loop mode."""
731
+ from code_muse.command_line.wiggum_state import is_wiggum_active, stop_wiggum
732
+ from code_muse.messaging import emit_info, emit_success
733
+
734
+ if is_wiggum_active():
735
+ stop_wiggum()
736
+ emit_success("🍩 Wiggum mode stopped!")
737
+ else:
738
+ emit_info("Wiggum mode is not active.")
739
+
740
+ return True