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,382 @@
1
+ """Stub extraction engine for the Autonomous Memory Pipeline.
2
+
3
+ Reads a session's messages file and produces a basic markdown summary.
4
+ Future iterations will drive this via a headless LLM agent.
5
+ """
6
+
7
+ import json
8
+ import logging
9
+ import os
10
+ import re
11
+ from dataclasses import dataclass
12
+ from datetime import UTC, datetime
13
+ from pathlib import Path
14
+ from typing import Any
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+ _MEMORY_RELEVANCE_THRESHOLD = float(
19
+ os.environ.get("MEMORY_RELEVANCE_THRESHOLD", "0.15")
20
+ )
21
+ _MEMORY_MIN_KEEP = int(os.environ.get("MEMORY_MIN_KEEP_CHUNKS", "3"))
22
+
23
+ # Optional BM25 scorer — gracefully degrades if module missing
24
+ _BM25_AVAILABLE = True
25
+ try:
26
+ from code_muse.plugins.autonomous_memory.bm25_scorer import (
27
+ BM25Scorer,
28
+ select_top_chunks,
29
+ )
30
+ except Exception:
31
+ _BM25_AVAILABLE = False
32
+
33
+ _ROLE_RE = re.compile(r'"role"\s*:\s*"(\w+)"')
34
+ _TOOL_RE = re.compile(r'"name"\s*:\s*"([^"]+)"')
35
+
36
+
37
+ @dataclass
38
+ class ExtractionResult:
39
+ """Output of extracting knowledge from a single session."""
40
+
41
+ session_path: str
42
+ raw_memory: str
43
+ synopsis: str
44
+ extracted_at: str
45
+
46
+
47
+ def _parse_messages_jsonl(path: Path) -> list[dict[str, Any]]:
48
+ """Best-effort parse of a JSONL messages file."""
49
+ messages: list[dict[str, Any]] = []
50
+ try:
51
+ with path.open("r", encoding="utf-8") as fh:
52
+ for line in fh:
53
+ line = line.strip()
54
+ if not line:
55
+ continue
56
+ try:
57
+ messages.append(json.loads(line))
58
+ except json.JSONDecodeError:
59
+ continue
60
+ except Exception as exc:
61
+ logger.warning(f"Failed to read messages file {path}: {exc}")
62
+ return messages
63
+
64
+
65
+ def _parse_messages_json(path: Path) -> list[dict[str, Any]]:
66
+ """Best-effort parse of a JSON array messages file."""
67
+ try:
68
+ with path.open("r", encoding="utf-8") as fh:
69
+ data = json.load(fh)
70
+ if isinstance(data, list):
71
+ return data
72
+ except Exception as exc:
73
+ logger.warning(f"Failed to read messages file {path}: {exc}")
74
+ return []
75
+
76
+
77
+ def _load_messages(path: Path) -> list[dict[str, Any]]:
78
+ """Load messages from either JSONL or JSON array format."""
79
+ if path.suffix == ".jsonl":
80
+ return _parse_messages_jsonl(path)
81
+ return _parse_messages_json(path)
82
+
83
+
84
+ def _count_roles(messages: list[dict[str, Any]]) -> dict[str, int]:
85
+ """Tally message roles."""
86
+ counts: dict[str, int] = {}
87
+ for msg in messages:
88
+ role = msg.get("role", "unknown")
89
+ counts[role] = counts.get(role, 0) + 1
90
+ return counts
91
+
92
+
93
+ def _extract_tools(messages: list[dict[str, Any]]) -> set[str]:
94
+ """Extract unique tool names from tool-call messages."""
95
+ tools: set[str] = set()
96
+ for msg in messages:
97
+ if msg.get("role") != "tool":
98
+ # Also look inside assistant messages for tool_calls
99
+ tool_calls = msg.get("tool_calls", [])
100
+ if isinstance(tool_calls, list):
101
+ for tc in tool_calls:
102
+ name = (
103
+ tc.get("function", {}).get("name")
104
+ if isinstance(tc, dict)
105
+ else None
106
+ )
107
+ if name:
108
+ tools.add(name)
109
+ elif isinstance(tc, dict):
110
+ name = tc.get("name")
111
+ if name:
112
+ tools.add(name)
113
+ continue
114
+ content = msg.get("content", "")
115
+ if isinstance(content, str):
116
+ match = _TOOL_RE.search(content)
117
+ if match:
118
+ tools.add(match.group(1))
119
+ name = msg.get("name") or msg.get("tool_name")
120
+ if isinstance(name, str):
121
+ tools.add(name)
122
+ return tools
123
+
124
+
125
+ def _collect_project_context(cwd: str | None = None) -> str:
126
+ """Collect project context from key files in the working directory.
127
+
128
+ Reads README, pyproject.toml/package.json, and key source files
129
+ to build a context string for relevance scoring.
130
+
131
+ Args:
132
+ cwd: Working directory. Uses current working dir if None.
133
+
134
+ Returns:
135
+ Context string (up to 2000 chars).
136
+ """
137
+ root = Path(cwd) if cwd else Path.cwd()
138
+ parts: list[str] = []
139
+
140
+ # Project name from directory
141
+ parts.append(f"Project: {root.name}")
142
+
143
+ # Read README (first 500 chars)
144
+ readme_paths = [
145
+ root / "README.md",
146
+ root / "README.rst",
147
+ root / "README",
148
+ ]
149
+ for rp in readme_paths:
150
+ if rp.exists():
151
+ try:
152
+ text = rp.read_text(encoding="utf-8", errors="replace")
153
+ parts.append(f"README: {text[:500]}")
154
+ except Exception:
155
+ pass
156
+ break
157
+
158
+ # Read project config
159
+ config_paths = [
160
+ root / "pyproject.toml",
161
+ root / "package.json",
162
+ root / "Cargo.toml",
163
+ root / "go.mod",
164
+ ]
165
+ for cp in config_paths:
166
+ if cp.exists():
167
+ try:
168
+ text = cp.read_text(encoding="utf-8", errors="replace")
169
+ parts.append(f"Config ({cp.name}): {text[:300]}")
170
+ except Exception:
171
+ pass
172
+ break
173
+
174
+ # Key source files (first 200 chars each, up to 5 files)
175
+ src_dirs = [root / "src", root / "lib", root / "app", root]
176
+ seen = 0
177
+ for src_dir in src_dirs:
178
+ if not src_dir.exists():
179
+ continue
180
+ for ext in (".py", ".js", ".ts", ".go", ".rs"):
181
+ for sf in sorted(src_dir.rglob(f"*{ext}"))[:3]:
182
+ try:
183
+ text = sf.read_text(encoding="utf-8", errors="replace")
184
+ parts.append(f"Source ({sf.name}): {text[:200]}")
185
+ seen += 1
186
+ if seen >= 5:
187
+ break
188
+ except Exception:
189
+ pass
190
+ if seen >= 5:
191
+ break
192
+ if seen >= 5:
193
+ break
194
+
195
+ context = "\n\n".join(parts)
196
+ return context[:2000]
197
+
198
+
199
+ def _split_into_chunks(text: str, max_chunk_lines: int = 30) -> list[str]:
200
+ """Split session transcript into turn-level chunks.
201
+
202
+ Each chunk is roughly one conversational turn (user/assistant pair)
203
+ or a block of up to max_chunk_lines.
204
+
205
+ Args:
206
+ text: Full transcript text.
207
+ max_chunk_lines: Maximum lines per chunk.
208
+
209
+ Returns:
210
+ List of chunk strings.
211
+ """
212
+ lines = text.split("\n")
213
+ chunks: list[str] = []
214
+ current: list[str] = []
215
+
216
+ for line in lines:
217
+ current.append(line)
218
+ # Split on common turn boundaries
219
+ if (
220
+ line.strip().startswith(("User:", "Assistant:", "Human:", "AI:", ">>>"))
221
+ or line.strip().startswith("---")
222
+ or len(current) >= max_chunk_lines
223
+ ):
224
+ chunk_text = "\n".join(current).strip()
225
+ if chunk_text and len(chunk_text) > 20:
226
+ chunks.append(chunk_text)
227
+ current = []
228
+
229
+ # Don't forget last chunk
230
+ if current:
231
+ chunk_text = "\n".join(current).strip()
232
+ if chunk_text and len(chunk_text) > 20:
233
+ chunks.append(chunk_text)
234
+
235
+ # If no chunks found, return whole text as one chunk
236
+ if not chunks:
237
+ return [text]
238
+
239
+ return chunks
240
+
241
+
242
+ def _messages_to_transcript(messages: list[dict[str, Any]]) -> str:
243
+ """Convert message list to a plain-text transcript."""
244
+ lines: list[str] = []
245
+ for msg in messages:
246
+ role = msg.get("role", "unknown")
247
+ content = msg.get("content", "")
248
+ if isinstance(content, str) and content.strip():
249
+ lines.append(f"{role.capitalize()}: {content.strip()}")
250
+ # Include tool call summaries
251
+ tool_calls = msg.get("tool_calls", [])
252
+ if isinstance(tool_calls, list):
253
+ for tc in tool_calls:
254
+ if not isinstance(tc, dict):
255
+ continue
256
+ name = tc.get("function", {}).get("name") or tc.get("name")
257
+ if name:
258
+ lines.append(f"Tool: {name}")
259
+ return "\n".join(lines)
260
+
261
+
262
+ def _extract_topics(messages: list[dict[str, Any]]) -> list[str]:
263
+ """Extract user message content snippets as 'topics'."""
264
+ topics: list[str] = []
265
+ for msg in messages:
266
+ if msg.get("role") != "user":
267
+ continue
268
+ content = msg.get("content", "")
269
+ if isinstance(content, str) and content.strip():
270
+ # Truncate long messages to first 120 chars for brevity
271
+ snippet = content.strip().replace("\n", " ")
272
+ if len(snippet) > 120:
273
+ snippet = snippet[:117] + "..."
274
+ topics.append(snippet)
275
+ # Deduplicate while preserving order
276
+ seen: set[str] = set()
277
+ deduped: list[str] = []
278
+ for t in topics:
279
+ if t not in seen:
280
+ seen.add(t)
281
+ deduped.append(t)
282
+ return deduped[:20] # Cap at 20 topics
283
+
284
+
285
+ def extract_session_knowledge(session_path: Path) -> ExtractionResult | None:
286
+ """Extract a basic memory summary from a session directory.
287
+
288
+ This is a stub implementation. Future work will replace it with a
289
+ headless LLM agent that performs deeper semantic extraction.
290
+ """
291
+ try:
292
+ messages_file = None
293
+ for candidate in (
294
+ session_path / "messages.json",
295
+ *session_path.glob("*.jsonl"),
296
+ ):
297
+ if candidate.exists():
298
+ messages_file = candidate
299
+ break
300
+
301
+ if messages_file is None:
302
+ logger.warning(f"No messages file found in {session_path}")
303
+ return None
304
+
305
+ messages = _load_messages(messages_file)
306
+ if not messages:
307
+ logger.warning(f"Empty or unreadable messages file in {session_path}")
308
+ return None
309
+
310
+ # --- Relevance scoring (added Epic 022) ---
311
+ # Convert messages to transcript, split into chunks, score against context
312
+ transcript = _messages_to_transcript(messages)
313
+ session_chunks = _split_into_chunks(transcript)
314
+
315
+ relevant_messages = messages
316
+ if _BM25_AVAILABLE and session_chunks:
317
+ try:
318
+ project_context = _collect_project_context()
319
+ scorer = BM25Scorer()
320
+ scorer.fit(session_chunks)
321
+ scores = scorer.score_batch(project_context, session_chunks)
322
+ relevant_chunks = select_top_chunks(
323
+ session_chunks,
324
+ scores,
325
+ threshold=_MEMORY_RELEVANCE_THRESHOLD,
326
+ min_keep=_MEMORY_MIN_KEEP,
327
+ )
328
+ logger.debug(
329
+ "Relevance scoring: %d/%d chunks kept (%.0f%%) for extraction",
330
+ len(relevant_chunks),
331
+ len(session_chunks),
332
+ len(relevant_chunks) / max(len(session_chunks), 1) * 100,
333
+ )
334
+ # Map relevant chunks back to messages for topic extraction
335
+ # A message is "relevant" if its content appears in a kept chunk
336
+ relevant_text = "\n".join(relevant_chunks)
337
+ relevant_messages = [
338
+ msg
339
+ for msg in messages
340
+ if isinstance(msg.get("content", ""), str)
341
+ and msg.get("content", "") in relevant_text
342
+ ]
343
+ # Ensure at least some messages remain for topic extraction
344
+ if not relevant_messages:
345
+ relevant_messages = messages[:_MEMORY_MIN_KEEP]
346
+ except Exception as exc:
347
+ logger.warning(
348
+ f"BM25 scoring failed, falling back to full transcript: {exc}"
349
+ )
350
+ relevant_messages = messages
351
+ # --- End relevance scoring ---
352
+
353
+ counts = _count_roles(messages)
354
+ tools = _extract_tools(messages)
355
+ topics = _extract_topics(relevant_messages)
356
+
357
+ user_count = counts.get("user", 0)
358
+ assistant_count = counts.get("assistant", 0)
359
+ tool_count = counts.get("tool", 0)
360
+
361
+ lines = [
362
+ "## Session Summary",
363
+ f"- Messages: {user_count} user, {assistant_count} assistant, {tool_count} tool",
364
+ ]
365
+ if tools:
366
+ lines.append(f"- Tools: {', '.join(sorted(tools))}")
367
+ if topics:
368
+ lines.append(f"- Topics: {'; '.join(topics)}")
369
+
370
+ raw_memory = "\n".join(lines)
371
+ synopsis = f"Session with {user_count} user messages, {len(tools)} tools, {len(topics)} topics"
372
+ extracted_at = datetime.now(UTC).isoformat()
373
+
374
+ return ExtractionResult(
375
+ session_path=str(session_path),
376
+ raw_memory=raw_memory,
377
+ synopsis=synopsis,
378
+ extracted_at=extracted_at,
379
+ )
380
+ except Exception as exc:
381
+ logger.warning(f"Extraction failed for {session_path}: {exc}")
382
+ return None
@@ -0,0 +1,105 @@
1
+ """Lease-based lock for the Autonomous Memory Pipeline.
2
+
3
+ Prevents concurrent memory extraction / consolidation jobs from
4
+ stepping on each other across multiple processes.
5
+ """
6
+
7
+ import json
8
+ import logging
9
+ import os
10
+ import time
11
+ from dataclasses import dataclass
12
+ from pathlib import Path
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ def _is_pid_running(pid: int) -> bool:
18
+ """Return ``True`` if process ``pid`` is alive."""
19
+ try:
20
+ os.kill(pid, 0)
21
+ except OSError:
22
+ return False
23
+ else:
24
+ return True
25
+
26
+
27
+ @dataclass
28
+ class LeaseHandle:
29
+ """Token representing a held memory lease."""
30
+
31
+ lock_path: Path
32
+ pid: int
33
+ acquired_at: float
34
+
35
+
36
+ def acquire_memory_lease(
37
+ memory_dir: Path, timeout_minutes: int = 30
38
+ ) -> LeaseHandle | None:
39
+ """Attempt to acquire the memory-processing lease.
40
+
41
+ Returns a :class:`LeaseHandle` on success, or ``None`` if the lease
42
+ is already held by a living process within the timeout window.
43
+ """
44
+ memory_dir.mkdir(parents=True, exist_ok=True)
45
+ lock_path = memory_dir / ".memory_lease"
46
+
47
+ # Check for existing lease
48
+ if lock_path.exists():
49
+ try:
50
+ with lock_path.open("r", encoding="utf-8") as fh:
51
+ data = json.load(fh)
52
+ old_pid = int(data.get("pid", 0))
53
+ acquired_at = float(data.get("acquired_at", 0))
54
+ except Exception:
55
+ old_pid = 0
56
+ acquired_at = 0
57
+
58
+ now = time.time()
59
+ expired = (now - acquired_at) > (timeout_minutes * 60)
60
+
61
+ if old_pid and _is_pid_running(old_pid) and not expired:
62
+ logger.debug(f"Memory lease held by PID {old_pid}")
63
+ return None
64
+
65
+ # Stale lease — break it
66
+ try:
67
+ lock_path.unlink()
68
+ logger.debug("Broke stale memory lease")
69
+ except OSError as exc:
70
+ logger.warning(f"Could not remove stale lock {lock_path}: {exc}")
71
+ return None
72
+
73
+ # Write new lease
74
+ my_pid = os.getpid()
75
+ acquired_at = time.time()
76
+ try:
77
+ with lock_path.open("w", encoding="utf-8") as fh:
78
+ json.dump({"pid": my_pid, "acquired_at": acquired_at}, fh)
79
+ except OSError as exc:
80
+ logger.warning(f"Could not write memory lease {lock_path}: {exc}")
81
+ return None
82
+
83
+ return LeaseHandle(lock_path=lock_path, pid=my_pid, acquired_at=acquired_at)
84
+
85
+
86
+ def release_lease(handle: LeaseHandle) -> None:
87
+ """Release a previously acquired memory lease."""
88
+ if not handle.lock_path.exists():
89
+ return
90
+
91
+ try:
92
+ with handle.lock_path.open("r", encoding="utf-8") as fh:
93
+ data = json.load(fh)
94
+ current_pid = int(data.get("pid", 0))
95
+ except Exception:
96
+ current_pid = 0
97
+
98
+ if current_pid == handle.pid:
99
+ try:
100
+ handle.lock_path.unlink()
101
+ logger.debug("Released memory lease")
102
+ except OSError as exc:
103
+ logger.warning(f"Could not release lease {handle.lock_path}: {exc}")
104
+ else:
105
+ logger.debug("Lease PID mismatch; not releasing")
@@ -0,0 +1,59 @@
1
+ """Memory injection for the Autonomous Memory Pipeline.
2
+
3
+ Loads the current project's memory summary (if fresh) and appends it to
4
+ system prompts for heuristic context.
5
+ """
6
+
7
+ import logging
8
+ import os
9
+ import time
10
+
11
+ from .session_scanner import get_memory_dir, get_project_hash
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+ FRESHNESS_DAYS = 7
16
+
17
+
18
+ def load_memory_injection(cwd: str | None = None) -> str | None:
19
+ """Load the memory summary for the current project if it is fresh.
20
+
21
+ Returns the memory text, or ``None`` if the file is missing or stale.
22
+ """
23
+ try:
24
+ cwd = cwd or os.getcwd()
25
+ project_hash = get_project_hash(cwd)
26
+ memory_dir = get_memory_dir(project_hash)
27
+ summary_path = memory_dir / "memory_summary.md"
28
+
29
+ if not summary_path.exists():
30
+ return None
31
+
32
+ mtime = summary_path.stat().st_mtime
33
+ age_days = (time.time() - mtime) / 86_400
34
+ if age_days > FRESHNESS_DAYS:
35
+ logger.debug(f"Memory summary stale ({age_days:.1f} days old)")
36
+ return None
37
+
38
+ text = summary_path.read_text(encoding="utf-8")
39
+ logger.info(f"Loaded memory injection from {summary_path}")
40
+ return text
41
+ except Exception as exc:
42
+ logger.warning(f"Failed to load memory injection: {exc}")
43
+ return None
44
+
45
+
46
+ def inject_into_system_prompt(base_prompt: str, memory_text: str) -> str:
47
+ """Append a memory section to ``base_prompt``.
48
+
49
+ Returns the combined prompt string.
50
+ """
51
+ section = (
52
+ "\n\n## Memory Guidance\n\n"
53
+ "The following is accumulated project knowledge from past sessions.\n"
54
+ "Treat it as heuristic context, not authoritative fact. Always prefer\n"
55
+ "current repo evidence over conflicting memory. Cite the memory path\n"
56
+ "(MEMORY.md) when you use remembered information.\n\n"
57
+ f"{memory_text}"
58
+ )
59
+ return base_prompt + section