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,438 @@
1
+ """Git compression strategies for the filter engine.
2
+
3
+ Parses common git command output and produces compact summaries.
4
+ """
5
+
6
+ import re
7
+
8
+ from code_muse.plugins.filter_engine.registry import get_registry
9
+ from code_muse.plugins.filter_engine.verbosity import VerbosityLevel
10
+ from code_muse.tools.command_runner import ShellCommandOutput
11
+
12
+
13
+ def _compress_plain_git_status(
14
+ stdout: str,
15
+ stderr: str,
16
+ verbosity: VerbosityLevel,
17
+ ) -> ShellCommandOutput:
18
+ """Fallback parser for human-readable ``git status`` output."""
19
+ lines = stdout.strip().splitlines()
20
+
21
+ cdef str branch = "unknown"
22
+ cdef int staged = 0
23
+ cdef int unstaged = 0
24
+ cdef int untracked = 0
25
+
26
+ cdef bint in_staged = False
27
+ cdef bint in_unstaged = False
28
+ cdef bint in_untracked = False
29
+ cdef str line
30
+ cdef str raw
31
+
32
+ for raw in lines:
33
+ line = raw.strip()
34
+ if line.startswith("On branch "):
35
+ branch = line[10:].strip()
36
+ elif line.startswith("HEAD detached "):
37
+ branch = line[14:].strip().split()[0]
38
+ elif line == "Changes to be committed:":
39
+ in_staged = True
40
+ in_unstaged = False
41
+ in_untracked = False
42
+ elif line == "Changes not staged for commit:":
43
+ in_staged = False
44
+ in_unstaged = True
45
+ in_untracked = False
46
+ elif line == "Untracked files:":
47
+ in_staged = False
48
+ in_unstaged = False
49
+ in_untracked = True
50
+ elif line == "":
51
+ in_staged = in_unstaged = in_untracked = False
52
+ elif line.startswith("("):
53
+ continue
54
+ elif in_staged and any(
55
+ line.startswith(p)
56
+ for p in ("modified:", "new file:", "deleted:", "renamed:", "copied:")
57
+ ):
58
+ staged += 1
59
+ elif in_unstaged and any(
60
+ line.startswith(p) for p in ("modified:", "deleted:", "renamed:")
61
+ ):
62
+ unstaged += 1
63
+ elif in_untracked and not line.startswith("("):
64
+ untracked += 1
65
+
66
+ summary = (
67
+ f"branch:{branch} | staged:{staged} unstaged:{unstaged} untracked:{untracked}"
68
+ )
69
+ if verbosity >= VerbosityLevel.VERBOSE:
70
+ summary += f"\n{stdout.strip()}"
71
+
72
+ success = not stderr.strip() or "error" not in stderr.lower()
73
+ return ShellCommandOutput(
74
+ success=success,
75
+ command="git status",
76
+ stdout=summary,
77
+ stderr=stderr if verbosity >= VerbosityLevel.VERY_VERBOSE else None,
78
+ exit_code=0,
79
+ execution_time=None,
80
+ )
81
+
82
+
83
+ def compress_git_status(
84
+ stdout: str,
85
+ stderr: str,
86
+ verbosity: VerbosityLevel,
87
+ ) -> ShellCommandOutput:
88
+ """Compress ``git status`` output into a compact summary.
89
+
90
+ Parses porcelain-style status lines and counts staged, unstaged, and
91
+ untracked files. Also extracts branch name and ahead/behind counts.
92
+
93
+ Falls back to a plain-text parser when the output does not look like
94
+ porcelain (e.g. the user ran ``git status`` without ``--porcelain``).
95
+
96
+ Args:
97
+ stdout: Raw command stdout.
98
+ stderr: Raw command stderr.
99
+ verbosity: Current verbosity level.
100
+
101
+ Returns:
102
+ A :class:`ShellCommandOutput` with the compressed summary.
103
+ """
104
+ lines = stdout.strip().splitlines()
105
+
106
+ # Detect plain (human-readable) git status output
107
+ if lines and (
108
+ lines[0].startswith("On branch ")
109
+ or lines[0].startswith("HEAD detached ")
110
+ or lines[0].startswith("nothing to commit")
111
+ ):
112
+ return _compress_plain_git_status(stdout, stderr, verbosity)
113
+
114
+ cdef str branch = "unknown"
115
+ cdef int ahead = 0
116
+ cdef int behind = 0
117
+
118
+ cdef int staged = 0
119
+ cdef int unstaged = 0
120
+ cdef int untracked = 0
121
+
122
+ file_lists: dict[str, list[str]] = {
123
+ "M": [],
124
+ "A": [],
125
+ "D": [],
126
+ "??": [],
127
+ }
128
+
129
+ cdef str line
130
+ cdef str xy
131
+ cdef str filename
132
+ cdef str index_char
133
+ cdef str worktree_char
134
+ cdef object branch_match
135
+ cdef object ahead_match
136
+ cdef object behind_match
137
+
138
+ for line in lines:
139
+ line = line.rstrip("\r")
140
+ if line.startswith("##"):
141
+ # Branch line: ## main...origin/main [ahead 2, behind 1]
142
+ branch_match = re.search(r"##\s+([^.\s]+)", line)
143
+ if branch_match:
144
+ branch = branch_match.group(1)
145
+ ahead_match = re.search(r"ahead\s+(\d+)", line)
146
+ if ahead_match:
147
+ ahead = int(ahead_match.group(1))
148
+ behind_match = re.search(r"behind\s+(\d+)", line)
149
+ if behind_match:
150
+ behind = int(behind_match.group(1))
151
+ continue
152
+
153
+ if len(line) >= 2 and (line.startswith("??") or line[0] in " MADRC"):
154
+ # Porcelain status line: XY filename
155
+ xy = line[:2]
156
+ filename = line[3:] if len(line) > 3 else ""
157
+
158
+ if xy == "??":
159
+ untracked += 1
160
+ file_lists["??"].append(filename)
161
+ else:
162
+ index_char = xy[0]
163
+ worktree_char = xy[1]
164
+ if index_char in "MADRC":
165
+ staged += 1
166
+ if index_char in file_lists:
167
+ file_lists[index_char].append(filename)
168
+ if worktree_char in "MADRC":
169
+ unstaged += 1
170
+ if worktree_char in file_lists:
171
+ file_lists[worktree_char].append(filename)
172
+
173
+ summary = (
174
+ f"branch:{branch} ↑{ahead} ↓{behind} | "
175
+ f"M:{len(file_lists['M'])} A:{len(file_lists['A'])} "
176
+ f"D:{len(file_lists['D'])} ??{len(file_lists['??'])}"
177
+ )
178
+
179
+ if verbosity >= VerbosityLevel.VERBOSE:
180
+ parts = [summary]
181
+ for key, files in file_lists.items():
182
+ if files:
183
+ parts.append(f"{key}: {', '.join(files[:20])}")
184
+ if len(files) > 20:
185
+ parts.append(f" ... and {len(files) - 20} more")
186
+ summary = "\n".join(parts)
187
+
188
+ success = not stderr.strip() or "error" not in stderr.lower()
189
+ return ShellCommandOutput(
190
+ success=success,
191
+ command="git status",
192
+ stdout=summary,
193
+ stderr=stderr if verbosity >= VerbosityLevel.VERY_VERBOSE else None,
194
+ exit_code=0,
195
+ execution_time=None,
196
+ )
197
+
198
+
199
+ def compress_git_log(
200
+ stdout: str,
201
+ stderr: str,
202
+ verbosity: VerbosityLevel,
203
+ ) -> ShellCommandOutput:
204
+ """Compress ``git log --oneline`` output into a compact summary.
205
+
206
+ Args:
207
+ stdout: Raw command stdout.
208
+ stderr: Raw command stderr.
209
+ verbosity: Current verbosity level.
210
+
211
+ Returns:
212
+ A :class:`ShellCommandOutput` with the compressed summary.
213
+ """
214
+ lines = [line for line in stdout.strip().splitlines() if line.strip()]
215
+ cdef int count = len(lines)
216
+
217
+ if count == 0:
218
+ return ShellCommandOutput(
219
+ success=True,
220
+ command="git log",
221
+ stdout="0 commits",
222
+ stderr=stderr if verbosity >= VerbosityLevel.VERY_VERBOSE else None,
223
+ exit_code=0,
224
+ execution_time=None,
225
+ )
226
+
227
+ cdef str first_hash = lines[0][:7] if lines[0] else ""
228
+ cdef str last_hash = lines[-1][:7] if lines[-1] else ""
229
+
230
+ # Extract commit messages (after the hash)
231
+ messages: list[str] = []
232
+ cdef list parts
233
+ cdef str line
234
+ cdef str msg
235
+
236
+ for line in lines:
237
+ parts = line.split(None, 1)
238
+ if len(parts) > 1:
239
+ messages.append(parts[1])
240
+ else:
241
+ messages.append(line)
242
+
243
+ preview = ", ".join(messages[:3])
244
+ if len(messages) > 3:
245
+ preview += f", ... ({len(messages) - 3} more)"
246
+
247
+ summary = f"{count} commits ({first_hash}..{last_hash}): {preview}"
248
+
249
+ if verbosity >= VerbosityLevel.VERBOSE:
250
+ summary = f"{count} commits ({first_hash}..{last_hash}):\n" + "\n".join(
251
+ f" {line[:7]} {msg}" for line, msg in zip(lines, messages, strict=False)
252
+ )
253
+
254
+ success = not stderr.strip() or "error" not in stderr.lower()
255
+ return ShellCommandOutput(
256
+ success=success,
257
+ command="git log",
258
+ stdout=summary,
259
+ stderr=stderr if verbosity >= VerbosityLevel.VERY_VERBOSE else None,
260
+ exit_code=0,
261
+ execution_time=None,
262
+ )
263
+
264
+
265
+ def compress_git_diff(
266
+ stdout: str,
267
+ stderr: str,
268
+ verbosity: VerbosityLevel,
269
+ ) -> ShellCommandOutput:
270
+ """Compress ``git diff`` output into a compact summary.
271
+
272
+ When *verbosity* is :attr:`~VerbosityLevel.VERBOSE` or higher the raw
273
+ diff is preserved. Otherwise parses ``--stat`` style output or falls
274
+ back to counting changed hunks.
275
+
276
+ Args:
277
+ stdout: Raw command stdout.
278
+ stderr: Raw command stderr.
279
+ verbosity: Current verbosity level.
280
+
281
+ Returns:
282
+ A :class:`ShellCommandOutput` with the compressed summary.
283
+ """
284
+ if verbosity >= VerbosityLevel.VERBOSE:
285
+ # Return full diff at -v and above
286
+ success = not stderr.strip() or "error" not in stderr.lower()
287
+ return ShellCommandOutput(
288
+ success=success,
289
+ command="git diff",
290
+ stdout=stdout,
291
+ stderr=stderr,
292
+ exit_code=0,
293
+ execution_time=None,
294
+ )
295
+
296
+ cdef int files_changed = 0
297
+ cdef int insertions = 0
298
+ cdef int deletions = 0
299
+ cdef bint stat_found = False
300
+
301
+ stat_pattern = re.compile(
302
+ r"^\s*(\d+) files? changed, (\d+) insertions?\(\+\), (\d+) deletions?\(-\)"
303
+ )
304
+
305
+ cdef str line
306
+ cdef object match
307
+
308
+ for line in stdout.splitlines():
309
+ if not stat_found:
310
+ match = stat_pattern.search(line)
311
+ if match:
312
+ files_changed = int(match.group(1))
313
+ insertions = int(match.group(2))
314
+ deletions = int(match.group(3))
315
+ stat_found = True
316
+ continue
317
+ if line.startswith("diff --git"):
318
+ files_changed += 1
319
+ elif line.startswith("+") and not line.startswith("+++"):
320
+ insertions += 1
321
+ elif line.startswith("-") and not line.startswith("---"):
322
+ deletions += 1
323
+
324
+ summary = (
325
+ f"{files_changed} files changed, {insertions} "
326
+ f"insertions(+), {deletions} deletions(-)"
327
+ )
328
+
329
+ success = not stderr.strip() or "error" not in stderr.lower()
330
+ return ShellCommandOutput(
331
+ success=success,
332
+ command="git diff",
333
+ stdout=summary,
334
+ stderr=stderr if verbosity >= VerbosityLevel.VERY_VERBOSE else None,
335
+ exit_code=0,
336
+ execution_time=None,
337
+ )
338
+
339
+
340
+ def compress_git_mutation(
341
+ stdout: str,
342
+ stderr: str,
343
+ verbosity: VerbosityLevel,
344
+ ) -> ShellCommandOutput:
345
+ """Compress output from mutating git commands (add, commit, push, pull, etc.).
346
+
347
+ Returns a minimal ``"ok"`` or ``"ok <hash>"`` summary. On error the
348
+ stderr is surfaced.
349
+
350
+ Args:
351
+ stdout: Raw command stdout.
352
+ stderr: Raw command stderr.
353
+ verbosity: Current verbosity level.
354
+ """
355
+ cdef bint has_error = bool(stderr.strip()) and (
356
+ "error" in stderr.lower()
357
+ or "fatal" in stderr.lower()
358
+ or "conflict" in stderr.lower()
359
+ )
360
+
361
+ if has_error:
362
+ error_msg = stderr.strip().splitlines()[0] if stderr.strip() else "Git error"
363
+ summary = f"ERROR: {error_msg}"
364
+ return ShellCommandOutput(
365
+ success=False,
366
+ command="git",
367
+ stdout=None,
368
+ stderr=summary,
369
+ exit_code=1,
370
+ execution_time=None,
371
+ )
372
+
373
+ # Try to extract a commit hash from stdout
374
+ cdef object hash_match = re.search(r"\b([a-f0-9]{7,40})\b", stdout)
375
+ cdef str commit_hash = hash_match.group(1)[:7] if hash_match else ""
376
+
377
+ summary = f"ok {commit_hash}" if commit_hash else "ok"
378
+
379
+ if verbosity >= VerbosityLevel.VERBOSE and stdout.strip():
380
+ summary += f"\n{stdout.strip()}"
381
+
382
+ return ShellCommandOutput(
383
+ success=True,
384
+ command="git",
385
+ stdout=summary,
386
+ stderr=None,
387
+ exit_code=0,
388
+ execution_time=None,
389
+ )
390
+
391
+
392
+ def compress_git(
393
+ command: str,
394
+ stdout: str,
395
+ stderr: str,
396
+ exit_code: int,
397
+ verbosity: VerbosityLevel,
398
+ ) -> ShellCommandOutput | None:
399
+ """Main dispatcher for git compression strategies.
400
+
401
+ Args:
402
+ command: The original git command.
403
+ stdout: Raw stdout from the command.
404
+ stderr: Raw stderr from the command.
405
+ exit_code: Process exit code.
406
+ verbosity: Current verbosity level.
407
+
408
+ Returns:
409
+ A compressed :class:`ShellCommandOutput` or ``None`` on error.
410
+ """
411
+ cdef str stripped = command.strip()
412
+ cdef object mutation_pattern
413
+
414
+ # Determine subcommand
415
+ if re.search(r"git\s+status", stripped):
416
+ return compress_git_status(stdout, stderr, verbosity)
417
+
418
+ if re.search(r"git\s+log", stripped):
419
+ return compress_git_log(stdout, stderr, verbosity)
420
+
421
+ if re.search(r"git\s+diff", stripped):
422
+ return compress_git_diff(stdout, stderr, verbosity)
423
+
424
+ # Mutations: add, commit, push, pull, fetch, merge, rebase, stash, reset, etc.
425
+ mutation_pattern = re.compile(
426
+ r"git\s+(add|commit|push|pull|fetch|merge|rebase|stash|reset|checkout|branch|tag|remote|init|clone)"
427
+ )
428
+ if mutation_pattern.search(stripped):
429
+ return compress_git_mutation(stdout, stderr, verbosity)
430
+
431
+ # Fallback: treat everything else as a mutation
432
+ return compress_git_mutation(stdout, stderr, verbosity)
433
+
434
+
435
+ # ---------------------------------------------------------------------------
436
+ # Register with the strategy registry
437
+ # ---------------------------------------------------------------------------
438
+ get_registry().register("git", compress_git, priority=0)
@@ -0,0 +1,253 @@
1
+ """SmartCrusher JSON compression engine.
2
+
3
+ Takes analyzed JSON patterns and produces compact output.
4
+ """
5
+
6
+ import json
7
+ from typing import Any
8
+
9
+ from code_muse.plugins.filter_engine.strategies.json_patterns import analyze_json
10
+ from code_muse.plugins.filter_engine.verbosity import VerbosityLevel
11
+
12
+
13
+ def _format_compact(obj: Any) -> str:
14
+ """Format a value as compact JSON-like string (no unnecessary whitespace).
15
+
16
+ Uses an explicit stack instead of recursion to avoid deep-call overhead
17
+ and temporary object churn on nested structures.
18
+ """
19
+ cdef list result
20
+ cdef list stack
21
+ cdef str kind
22
+ cdef object data
23
+ cdef object value
24
+ cdef list items
25
+ cdef object k
26
+ cdef object v
27
+ cdef int i
28
+ cdef str first
29
+ cdef str second
30
+
31
+ # Scalar fast-paths — return immediately without stack overhead.
32
+ if isinstance(obj, str):
33
+ return json.dumps(obj)
34
+ if isinstance(obj, bool):
35
+ return "true" if obj else "false"
36
+ if isinstance(obj, (int, float)):
37
+ return str(obj)
38
+ if obj is None:
39
+ return "null"
40
+
41
+ # Iterative walk for containers.
42
+ result = []
43
+ # Stack entries: (kind, data)
44
+ # "val" -> data is any value to format
45
+ # "raw" -> data is a string to append verbatim
46
+ # "cdict" -> append "}"
47
+ # "clist" -> append "]"
48
+ stack = [("val", obj)]
49
+
50
+ while stack:
51
+ kind, data = stack.pop()
52
+ if kind == "raw":
53
+ result.append(data)
54
+ continue
55
+ if kind == "cdict":
56
+ result.append("}")
57
+ continue
58
+ if kind == "clist":
59
+ result.append("]")
60
+ continue
61
+
62
+ # kind == "val"
63
+ value = data
64
+ if isinstance(value, str):
65
+ result.append(json.dumps(value))
66
+ elif isinstance(value, bool):
67
+ result.append("true" if value else "false")
68
+ elif isinstance(value, (int, float)):
69
+ result.append(str(value))
70
+ elif value is None:
71
+ result.append("null")
72
+ elif isinstance(value, dict):
73
+ if not value:
74
+ result.append("{}")
75
+ else:
76
+ result.append("{")
77
+ stack.append(("cdict", None))
78
+ items = list(value.items())
79
+ for i in range(len(items) - 1, -1, -1):
80
+ k, v = items[i]
81
+ stack.append(("val", v))
82
+ stack.append(("raw", f"{json.dumps(k)}:"))
83
+ if i > 0:
84
+ stack.append(("raw", ","))
85
+ elif isinstance(value, list):
86
+ if not value:
87
+ result.append("[]")
88
+ elif len(value) <= 3:
89
+ result.append("[")
90
+ stack.append(("clist", None))
91
+ for i in range(len(value) - 1, -1, -1):
92
+ stack.append(("val", value[i]))
93
+ if i > 0:
94
+ stack.append(("raw", ","))
95
+ else:
96
+ # Long list — inline first two items (still iterative).
97
+ first = _format_compact(value[0])
98
+ second = _format_compact(value[1])
99
+ result.append(f"[{first},{second},...{len(value)}items]")
100
+ else:
101
+ result.append(json.dumps(value))
102
+
103
+ return "".join(result)
104
+
105
+
106
+ def compress_json(
107
+ data: Any,
108
+ verbosity: VerbosityLevel | int = VerbosityLevel.COMPACT,
109
+ max_output_chars: int = 4000,
110
+ ) -> str:
111
+ """Compress JSON data using SmartCrusher.
112
+
113
+ Args:
114
+ data: Parsed JSON (dict, list, or scalar).
115
+ verbosity: Compression level. 0 = max, 4 = near-original.
116
+ max_output_chars: Hard output limit.
117
+
118
+ Returns:
119
+ Compact JSON string.
120
+ """
121
+ if isinstance(verbosity, int):
122
+ level = verbosity
123
+ else:
124
+ level = verbosity.value if hasattr(verbosity, "value") else 1
125
+
126
+ if not isinstance(data, (dict, list)):
127
+ # Scalar — just return it compactly
128
+ return json.dumps(data)
129
+
130
+ analysis = analyze_json(data)
131
+
132
+ # If it's a homogeneous array with template, use template compression
133
+ if analysis["is_homogeneous"] and analysis["template"] and isinstance(data, list):
134
+ return _compress_homogeneous_array(data, analysis, level, max_output_chars)
135
+
136
+ # Otherwise, compact formatting
137
+ if isinstance(data, dict):
138
+ return _compress_dict(data, analysis, level, max_output_chars)
139
+ elif isinstance(data, list):
140
+ return _compress_list(data, analysis, level, max_output_chars)
141
+
142
+ return json.dumps(data)
143
+
144
+
145
+ def _compress_homogeneous_array(
146
+ data: list[dict],
147
+ analysis: dict[str, Any],
148
+ level: int,
149
+ max_chars: int,
150
+ ) -> str:
151
+ """Compress array of same-shape dicts using template notation."""
152
+ template = analysis["template"]
153
+ field_scores = analysis["field_scores"]
154
+
155
+ # Select fields based on verbosity
156
+ keep_keys = _select_fields(template, field_scores, level)
157
+
158
+ if level == 0:
159
+ # Ultra-compact: template + values array only
160
+ key_order = [k for k in keep_keys if k in template]
161
+ values = []
162
+ for item in data:
163
+ row = [item.get(k) for k in key_order]
164
+ values.append(row)
165
+ template_str = ",".join(key_order)
166
+ compact_values = _format_compact(values)
167
+ result = f"{{@{template_str}}}{compact_values}"
168
+ return result[:max_chars]
169
+
170
+ # Level 1+: keep some structure
171
+ if level >= 1:
172
+ compact_items = []
173
+ for item in data:
174
+ kept = {k: item.get(k) for k in keep_keys if k in item}
175
+ compact_items.append(_format_compact(kept))
176
+ inner = ",".join(compact_items[:20]) # limit to 20 items shown
177
+ if len(data) > 20:
178
+ inner += f",...{len(data) - 20}more"
179
+ return f"[{inner}]"[:max_chars]
180
+
181
+ return json.dumps(data)[:max_chars]
182
+
183
+
184
+ def _compress_dict(
185
+ data: dict,
186
+ analysis: dict[str, Any],
187
+ level: int,
188
+ max_chars: int,
189
+ ) -> str:
190
+ """Compress a single dict."""
191
+ field_scores = analysis["field_scores"] or {}
192
+ if not field_scores:
193
+ # Simple case: build field scores from data
194
+ field_scores = {}
195
+ for key in data:
196
+ if key.lower() in ("error", "exception"):
197
+ field_scores[key] = 1.0
198
+ elif key.startswith("_"):
199
+ field_scores[key] = 0.1
200
+ else:
201
+ field_scores[key] = 0.6
202
+
203
+ keep_keys = _select_fields(data, field_scores, level)
204
+
205
+ if level == 0:
206
+ kept = {k: data[k] for k in keep_keys if k in data}
207
+ if not kept:
208
+ # Fallback: keep all keys but format compactly
209
+ kept = {k: data[k] for k in data}
210
+ return _format_compact(kept)[:max_chars]
211
+
212
+ if level >= 1:
213
+ return json.dumps(data)[:max_chars]
214
+
215
+ return json.dumps(data)[:max_chars]
216
+
217
+
218
+ def _compress_list(
219
+ data: list,
220
+ analysis: dict[str, Any],
221
+ level: int,
222
+ max_chars: int,
223
+ ) -> str:
224
+ """Compress a generic list (non-homogeneous)."""
225
+ if level == 0:
226
+ if len(data) <= 5:
227
+ return _format_compact(data)[:max_chars]
228
+ return f"[{len(data)} items]"[:max_chars]
229
+ if level >= 1:
230
+ return json.dumps(data)[:max_chars]
231
+ return json.dumps(data)[:max_chars]
232
+
233
+
234
+ def _select_fields(
235
+ schema: dict[str, Any] | None,
236
+ scores: dict[str, float],
237
+ level: int,
238
+ ) -> list[str]:
239
+ """Select which fields to keep based on verbosity level and scores.
240
+
241
+ Level 0: keep only score >= 0.7 (names, errors)
242
+ Level 1: keep score >= 0.5
243
+ Level 2: keep score >= 0.3
244
+ Level 3: keep score >= 0.1
245
+ Level 4: keep all
246
+ """
247
+ thresholds = {0: 0.7, 1: 0.5, 2: 0.3, 3: 0.1, 4: 0.0}
248
+ threshold = thresholds.get(level, 0.3)
249
+
250
+ if not schema:
251
+ return []
252
+
253
+ return [k for k, v in scores.items() if v >= threshold]