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,361 @@
1
+ """Event stream handler for processing streaming events from agent runs."""
2
+
3
+ import asyncio
4
+ import logging
5
+ import math
6
+ from collections.abc import AsyncIterable
7
+ from typing import Any
8
+
9
+ from pydantic_ai import PartDeltaEvent, PartEndEvent, PartStartEvent, RunContext
10
+ from pydantic_ai.messages import (
11
+ TextPart,
12
+ TextPartDelta,
13
+ ThinkingPart,
14
+ ThinkingPartDelta,
15
+ ToolCallPart,
16
+ ToolCallPartDelta,
17
+ )
18
+ from rich.console import Console
19
+ from rich.markup import escape
20
+ from rich.text import Text
21
+
22
+ from code_muse.config import get_banner_color, get_subagent_verbose
23
+ from code_muse.messaging.spinner import pause_all_spinners, resume_all_spinners
24
+ from code_muse.tools.subagent_context import is_subagent
25
+
26
+ logger = logging.getLogger(__name__)
27
+
28
+
29
+ def _handle_stream_task_exception(task: asyncio.Task) -> None:
30
+ """Consume any exception from a fire-and-forget stream-event task."""
31
+ try:
32
+ task.result()
33
+ except Exception:
34
+ logger.debug("Stream event callback failed", exc_info=True)
35
+
36
+
37
+ def _fire_stream_event(event_type: str, event_data: Any) -> None:
38
+ """Fire a stream event callback asynchronously (non-blocking).
39
+
40
+ PERF-08: Only fires for structural events (part_start, part_end).
41
+ High-frequency part_delta events are skipped since no current plugin
42
+ consumes them, and creating 100+ micro-tasks per response is wasteful.
43
+ """
44
+ if event_type == "part_delta":
45
+ return
46
+ try:
47
+ from code_muse import callbacks
48
+ from code_muse.messaging import get_session_context
49
+
50
+ agent_session_id = get_session_context()
51
+ task = asyncio.create_task(
52
+ callbacks.on_stream_event(event_type, event_data, agent_session_id)
53
+ )
54
+ task.add_done_callback(_handle_stream_task_exception)
55
+ except ImportError:
56
+ logger.debug("callbacks or messaging module not available for stream event")
57
+ except Exception as e:
58
+ logger.debug("Error firing stream event callback: %s", e)
59
+
60
+
61
+ # Module-level console for streaming output
62
+ # Set via set_streaming_console() to share console with spinner
63
+ _streaming_console: Console | None = None
64
+
65
+
66
+ def set_streaming_console(console: Console | None) -> None:
67
+ """Set the console used for streaming output.
68
+
69
+ This should be called with the same console used by the spinner
70
+ to avoid Live display conflicts that cause line duplication.
71
+
72
+ Args:
73
+ console: The Rich console to use, or None to use a fallback.
74
+ """
75
+ global _streaming_console
76
+ _streaming_console = console
77
+
78
+
79
+ def get_streaming_console() -> Console:
80
+ """Get the console for streaming output.
81
+
82
+ Returns the configured console or creates a fallback Console.
83
+ """
84
+ if _streaming_console is not None:
85
+ return _streaming_console
86
+ return Console()
87
+
88
+
89
+ def _should_suppress_output() -> bool:
90
+ """Check if sub-agent output should be suppressed.
91
+
92
+ Returns:
93
+ True if we're in a sub-agent context and verbose mode is disabled.
94
+ """
95
+ return is_subagent() and not get_subagent_verbose()
96
+
97
+
98
+ async def event_stream_handler(
99
+ ctx: RunContext,
100
+ events: AsyncIterable[Any],
101
+ ) -> None:
102
+ """Handle streaming events from the agent run.
103
+
104
+ This function processes streaming events and emits TextPart, ThinkingPart,
105
+ and ToolCallPart content with styled banners/tokens as they stream in.
106
+
107
+ Args:
108
+ ctx: The run context.
109
+ events: Async iterable of streaming events (PartStartEvent, PartDeltaEvent, etc.).
110
+ """
111
+ # If we're in a sub-agent and verbose mode is disabled, silently consume events
112
+ if _should_suppress_output():
113
+ async for _ in events:
114
+ pass # Just consume events without rendering
115
+ return
116
+
117
+ from termflow import Parser as TermflowParser
118
+ from termflow import Renderer as TermflowRenderer
119
+ from termflow.render.style import RenderFeatures
120
+
121
+ # Use the module-level console (set via set_streaming_console)
122
+ console = get_streaming_console()
123
+
124
+ # Track which part indices we're currently streaming (for Text/Thinking/Tool parts)
125
+ streaming_parts: set[int] = set()
126
+ thinking_parts: set[int] = set() # Track which parts are thinking (for dim style)
127
+ text_parts: set[int] = set() # Track which parts are text
128
+ tool_parts: set[int] = set() # Track which parts are tool calls
129
+ banner_printed: set[int] = set() # Track if banner was already printed
130
+ token_count: dict[int, int] = {} # Track token count per text/tool part
131
+ tool_names: dict[int, str] = {} # Track tool name per tool part index
132
+ did_stream_anything = False # Track if we streamed any content
133
+
134
+ # Termflow streaming state for text parts
135
+ termflow_parsers: dict[int, TermflowParser] = {}
136
+ termflow_renderers: dict[int, TermflowRenderer] = {}
137
+ termflow_line_buffers: dict[int, str] = {} # Buffer incomplete lines
138
+
139
+ async def _print_thinking_banner() -> None:
140
+ """Print the THINKING banner with spinner pause and line clear."""
141
+ nonlocal did_stream_anything
142
+
143
+ pause_all_spinners()
144
+ await asyncio.sleep(0.1) # Delay to let spinner fully clear
145
+ # Clear line and print newline before banner
146
+ console.print(" " * 50, end="\r")
147
+ console.print() # Newline before banner
148
+ # Bold banner with configurable color and lightning bolt
149
+ thinking_color = get_banner_color("thinking")
150
+ console.print(
151
+ Text.from_markup(
152
+ f"[bold white on {thinking_color}] THINKING [/bold white on {thinking_color}] [dim]\u26a1 "
153
+ ),
154
+ end="",
155
+ )
156
+ did_stream_anything = True
157
+
158
+ async def _print_response_banner() -> None:
159
+ """Print the AGENT RESPONSE banner with spinner pause and line clear."""
160
+ nonlocal did_stream_anything
161
+
162
+ pause_all_spinners()
163
+ await asyncio.sleep(0.1) # Delay to let spinner fully clear
164
+ # Clear line and print newline before banner
165
+ console.print(" " * 50, end="\r")
166
+ console.print() # Newline before banner
167
+ response_color = get_banner_color("agent_response")
168
+ console.print(
169
+ Text.from_markup(
170
+ f"[bold white on {response_color}] AGENT RESPONSE [/bold white on {response_color}]"
171
+ )
172
+ )
173
+ did_stream_anything = True
174
+
175
+ async for event in events:
176
+ # PartStartEvent - register the part but defer banner until content arrives
177
+ if isinstance(event, PartStartEvent):
178
+ # Fire stream event callback for part_start
179
+ _fire_stream_event(
180
+ "part_start",
181
+ {
182
+ "index": event.index,
183
+ "part_type": type(event.part).__name__,
184
+ "part": event.part,
185
+ },
186
+ )
187
+
188
+ part = event.part
189
+ if isinstance(part, ThinkingPart):
190
+ streaming_parts.add(event.index)
191
+ thinking_parts.add(event.index)
192
+ # If there's initial content, print banner + content now
193
+ if part.content and part.content.strip():
194
+ await _print_thinking_banner()
195
+ escaped = escape(part.content)
196
+ console.print(f"[dim]{escaped}[/dim]", end="")
197
+ banner_printed.add(event.index)
198
+ elif isinstance(part, TextPart):
199
+ streaming_parts.add(event.index)
200
+ text_parts.add(event.index)
201
+ # Initialize termflow streaming for this text part
202
+ termflow_parsers[event.index] = TermflowParser()
203
+ termflow_renderers[event.index] = TermflowRenderer(
204
+ output=console.file,
205
+ width=console.width,
206
+ features=RenderFeatures(clipboard=False),
207
+ )
208
+ termflow_line_buffers[event.index] = ""
209
+ # Handle initial content if present
210
+ if part.content and part.content.strip():
211
+ await _print_response_banner()
212
+ banner_printed.add(event.index)
213
+ termflow_line_buffers[event.index] = part.content
214
+ elif isinstance(part, ToolCallPart):
215
+ streaming_parts.add(event.index)
216
+ tool_parts.add(event.index)
217
+ token_count[event.index] = 0 # Initialize token counter
218
+ # Capture tool name from the start event
219
+ tool_names[event.index] = part.tool_name or ""
220
+ # Track tool name for display
221
+ banner_printed.add(
222
+ event.index
223
+ ) # Use banner_printed to track if we've shown tool info
224
+
225
+ # PartDeltaEvent - stream the content as it arrives
226
+ elif isinstance(event, PartDeltaEvent):
227
+ # Fire stream event callback for part_delta
228
+ _fire_stream_event(
229
+ "part_delta",
230
+ {
231
+ "index": event.index,
232
+ "delta_type": type(event.delta).__name__,
233
+ "delta": event.delta,
234
+ },
235
+ )
236
+
237
+ if event.index in streaming_parts:
238
+ delta = event.delta
239
+ if isinstance(delta, (TextPartDelta, ThinkingPartDelta)):
240
+ if delta.content_delta:
241
+ # For text parts, stream markdown with termflow
242
+ if event.index in text_parts:
243
+ # Print banner on first content
244
+ if event.index not in banner_printed:
245
+ await _print_response_banner()
246
+ banner_printed.add(event.index)
247
+
248
+ # Add content to line buffer
249
+ termflow_line_buffers[event.index] += delta.content_delta
250
+
251
+ # Process complete lines
252
+ parser = termflow_parsers[event.index]
253
+ renderer = termflow_renderers[event.index]
254
+ buffer = termflow_line_buffers[event.index]
255
+
256
+ while "\n" in buffer:
257
+ line, buffer = buffer.split("\n", 1)
258
+ events_to_render = parser.parse_line(line)
259
+ renderer.render_all(events_to_render)
260
+
261
+ termflow_line_buffers[event.index] = buffer
262
+ else:
263
+ # For thinking parts, stream immediately (dim)
264
+ if event.index not in banner_printed:
265
+ await _print_thinking_banner()
266
+ banner_printed.add(event.index)
267
+ escaped = escape(delta.content_delta)
268
+ console.print(f"[dim]{escaped}[/dim]", end="")
269
+ elif isinstance(delta, ToolCallPartDelta):
270
+ # For tool calls, estimate tokens from args_delta content
271
+ # args_delta contains the streaming JSON arguments
272
+ args_delta = getattr(delta, "args_delta", "") or ""
273
+ if args_delta:
274
+ # Same 2.5 chars/token heuristic as BaseAgent and file_operations
275
+ estimated_tokens = max(1, math.floor(len(args_delta) / 2.5))
276
+ token_count[event.index] += estimated_tokens
277
+ else:
278
+ # Even empty deltas count as activity
279
+ token_count[event.index] += 1
280
+
281
+ # Update tool name if delta provides more of it
282
+ tool_name_delta = getattr(delta, "tool_name_delta", "") or ""
283
+ if tool_name_delta:
284
+ tool_names[event.index] = (
285
+ tool_names.get(event.index, "") + tool_name_delta
286
+ )
287
+
288
+ # Use stored tool name for display
289
+ tool_name = tool_names.get(event.index, "")
290
+ count = token_count[event.index]
291
+ # Display with tool wrench icon and tool name
292
+ if tool_name:
293
+ console.print(
294
+ f" \U0001f527 Calling {tool_name}... {count} token(s) ",
295
+ end="\r",
296
+ )
297
+ else:
298
+ console.print(
299
+ f" \U0001f527 Calling tool... {count} token(s) ",
300
+ end="\r",
301
+ )
302
+
303
+ # PartEndEvent - finish the streaming with a newline
304
+ elif isinstance(event, PartEndEvent):
305
+ # Fire stream event callback for part_end
306
+ _fire_stream_event(
307
+ "part_end",
308
+ {
309
+ "index": event.index,
310
+ "next_part_kind": getattr(event, "next_part_kind", None),
311
+ },
312
+ )
313
+
314
+ if event.index in streaming_parts:
315
+ # For text parts, finalize termflow rendering
316
+ if event.index in text_parts:
317
+ # Render any remaining buffered content
318
+ if event.index in termflow_parsers:
319
+ parser = termflow_parsers[event.index]
320
+ renderer = termflow_renderers[event.index]
321
+ remaining = termflow_line_buffers.get(event.index, "")
322
+
323
+ # Parse and render any remaining partial line
324
+ if remaining.strip():
325
+ events_to_render = parser.parse_line(remaining)
326
+ renderer.render_all(events_to_render)
327
+
328
+ # Finalize the parser to close any open blocks
329
+ final_events = parser.finalize()
330
+ renderer.render_all(final_events)
331
+
332
+ # Clean up termflow state
333
+ del termflow_parsers[event.index]
334
+ del termflow_renderers[event.index]
335
+ del termflow_line_buffers[event.index]
336
+ # For tool parts, clear the chunk counter line
337
+ elif event.index in tool_parts:
338
+ # Clear the chunk counter line by printing spaces and returning
339
+ console.print(" " * 50, end="\r")
340
+ # For thinking parts, just print newline
341
+ elif event.index in banner_printed:
342
+ console.print() # Final newline after streaming
343
+
344
+ # Clean up token count and tool names
345
+ token_count.pop(event.index, None)
346
+ tool_names.pop(event.index, None)
347
+ # Clean up all tracking sets
348
+ streaming_parts.discard(event.index)
349
+ thinking_parts.discard(event.index)
350
+ text_parts.discard(event.index)
351
+ tool_parts.discard(event.index)
352
+ banner_printed.discard(event.index)
353
+
354
+ # Resume spinner if next part is NOT text/thinking/tool (avoid race condition)
355
+ # If next part is None or handled differently, it's safe to resume
356
+ # Note: spinner itself handles blank line before appearing
357
+ next_kind = getattr(event, "next_part_kind", None)
358
+ if next_kind not in ("text", "thinking", "tool-call"):
359
+ resume_all_spinners()
360
+
361
+ # Spinner is resumed in PartEndEvent when appropriate (based on next_part_kind)
@@ -0,0 +1,201 @@
1
+ """JSON-based agent configuration system."""
2
+
3
+ import json
4
+ import logging
5
+ from pathlib import Path
6
+
7
+ from .base_agent import BaseAgent
8
+
9
+ logger = logging.getLogger(__name__)
10
+
11
+
12
+ class JSONAgent(BaseAgent):
13
+ """Agent configured from a JSON file."""
14
+
15
+ def __init__(self, json_path: str):
16
+ """Initialize agent from JSON file.
17
+
18
+ Args:
19
+ json_path: Path to the JSON configuration file.
20
+ """
21
+ super().__init__()
22
+ self.json_path = json_path
23
+ self._config = self._load_config()
24
+ self._validate_config()
25
+
26
+ def _load_config(self) -> dict:
27
+ """Load configuration from JSON file."""
28
+ try:
29
+ with open(self.json_path, encoding="utf-8") as f:
30
+ return json.load(f)
31
+ except (json.JSONDecodeError, FileNotFoundError) as e:
32
+ raise ValueError(
33
+ f"Failed to load JSON agent config from {self.json_path}: {e}"
34
+ ) from e
35
+
36
+ def _validate_config(self) -> None:
37
+ """Validate required fields in configuration."""
38
+ required_fields = ["name", "description", "system_prompt", "tools"]
39
+ for field in required_fields:
40
+ if field not in self._config:
41
+ raise ValueError(
42
+ f"Missing required field '{field}' in JSON agent config: {self.json_path}"
43
+ )
44
+
45
+ # Validate tools is a list
46
+ if not isinstance(self._config["tools"], list):
47
+ raise ValueError(
48
+ f"'tools' must be a list in JSON agent config: {self.json_path}"
49
+ )
50
+
51
+ # Validate system_prompt is string or list
52
+ system_prompt = self._config["system_prompt"]
53
+ if not isinstance(system_prompt, (str, list)):
54
+ raise ValueError(
55
+ f"'system_prompt' must be a string or list in JSON agent config: {self.json_path}"
56
+ )
57
+
58
+ @property
59
+ def name(self) -> str:
60
+ """Get agent name from JSON config."""
61
+ return self._config["name"]
62
+
63
+ @property
64
+ def display_name(self) -> str:
65
+ """Get display name from JSON config, fallback to name with emoji."""
66
+ return self._config.get("display_name", f"{self.name.title()} 🤖")
67
+
68
+ @property
69
+ def description(self) -> str:
70
+ """Get description from JSON config."""
71
+ return self._config["description"]
72
+
73
+ def get_system_prompt(self) -> str:
74
+ """Get system prompt from JSON config."""
75
+ system_prompt = self._config["system_prompt"]
76
+
77
+ # If it's a list, join with newlines
78
+ if isinstance(system_prompt, list):
79
+ return "\n".join(system_prompt)
80
+
81
+ return system_prompt
82
+
83
+ def get_available_tools(self) -> list[str]:
84
+ """Get available tools from JSON config.
85
+
86
+ Supports both built-in tools and Universal Constructor (UC) tools.
87
+ UC tools are identified by checking the UC registry.
88
+ """
89
+ from code_muse.tools import get_available_tool_names
90
+
91
+ available_tools = get_available_tool_names()
92
+
93
+ # Also get UC tool names
94
+ uc_tool_names = set()
95
+ try:
96
+ from code_muse.plugins.universal_constructor.registry import get_registry
97
+
98
+ registry = get_registry()
99
+ for tool in registry.list_tools():
100
+ if tool.meta.enabled:
101
+ uc_tool_names.add(tool.full_name)
102
+ except ImportError:
103
+ pass # UC module not available
104
+ except Exception as e:
105
+ # Log unexpected errors but don't fail
106
+ import logging
107
+
108
+ logging.debug(f"UC registry access failed: {e}")
109
+
110
+ # Return tools that are either built-in OR UC tools
111
+ requested_tools = []
112
+ for tool in self._config["tools"]:
113
+ if tool in available_tools:
114
+ requested_tools.append(tool)
115
+ elif tool in uc_tool_names:
116
+ # UC tool - mark it specially so base_agent knows to handle it
117
+ requested_tools.append(f"uc:{tool}")
118
+
119
+ return requested_tools
120
+
121
+ def get_user_prompt(self) -> str | None:
122
+ """Get custom user prompt from JSON config."""
123
+ return self._config.get("user_prompt")
124
+
125
+ def get_tools_config(self) -> dict | None:
126
+ """Get tool configuration from JSON config."""
127
+ return self._config.get("tools_config")
128
+
129
+ def refresh_config(self) -> None:
130
+ """Reload the agent configuration from disk.
131
+
132
+ This keeps long-lived agent instances in sync after external edits.
133
+ """
134
+ self._config = self._load_config()
135
+ self._validate_config()
136
+
137
+ def get_model_name(self) -> str | None:
138
+ """Get pinned model name from JSON config, if specified.
139
+
140
+ Returns:
141
+ Model name to use for this agent, or None to use global default.
142
+ """
143
+ result = self._config.get("model")
144
+ if result is None:
145
+ result = super().get_model_name()
146
+ return result
147
+
148
+
149
+ def discover_json_agents() -> dict[str, str]:
150
+ """Discover JSON agent files in the user's and project's agents directories.
151
+
152
+ Searches two locations:
153
+ 1. User agents directory (~/.muse/agents/)
154
+ 2. Project agents directory (<CWD>/.muse/agents/) - if it exists
155
+
156
+ Project agents take priority over user agents when names collide.
157
+
158
+ Returns:
159
+ dict mapping agent names to their JSON file paths.
160
+ """
161
+ from code_muse.config import (
162
+ get_project_agents_directory,
163
+ get_user_agents_directory,
164
+ )
165
+
166
+ agents: dict[str, str] = {}
167
+
168
+ # 1. Discover user-level agents first
169
+ user_agents_dir = Path(get_user_agents_directory())
170
+ if user_agents_dir.exists() and user_agents_dir.is_dir():
171
+ for json_file in user_agents_dir.glob("*.json"):
172
+ try:
173
+ agent = JSONAgent(str(json_file))
174
+ agents[agent.name] = str(json_file)
175
+ except Exception as e:
176
+ logger.debug(
177
+ "Skipping invalid user agent file: %s (reason: %s: %s)",
178
+ json_file,
179
+ type(e).__name__,
180
+ str(e),
181
+ )
182
+ continue
183
+
184
+ # 2. Discover project-level agents (overrides user agents on name collision)
185
+ project_agents_dir_str = get_project_agents_directory()
186
+ if project_agents_dir_str is not None:
187
+ project_agents_dir = Path(project_agents_dir_str)
188
+ for json_file in project_agents_dir.glob("*.json"):
189
+ try:
190
+ agent = JSONAgent(str(json_file))
191
+ agents[agent.name] = str(json_file)
192
+ except Exception as e:
193
+ logger.debug(
194
+ "Skipping invalid project agent file: %s (reason: %s: %s)",
195
+ json_file,
196
+ type(e).__name__,
197
+ str(e),
198
+ )
199
+ continue
200
+
201
+ return agents