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,184 @@
1
+ """Content type detector for shell command output.
2
+
3
+ Sniffs stdout to classify output as JSON, diff, log, HTML, code, or unknown.
4
+ Uses fast heuristics — no LLM calls.
5
+ """
6
+
7
+ import enum
8
+ import json
9
+ import re
10
+ from typing import ClassVar
11
+
12
+
13
+ class ContentType(enum.Enum):
14
+ """Output content types."""
15
+
16
+ JSON = "json"
17
+ DIFF = "diff"
18
+ LOG = "log"
19
+ HTML = "html"
20
+ CODE = "code"
21
+ SEARCH = "search"
22
+ UNKNOWN = "unknown"
23
+
24
+
25
+ class ContentTypeDetector:
26
+ """Detect content type from shell command stdout."""
27
+
28
+ # Pre-compiled patterns
29
+ DIFF_HEADER: ClassVar[re.Pattern] = re.compile(
30
+ r"^@@\s+-(\d+),?\d*\s+\+(\d+),?\d*\s+@@", re.MULTILINE
31
+ )
32
+ LOG_PATTERNS: ClassVar[list[re.Pattern]] = [
33
+ re.compile(r"^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}"), # ISO timestamp
34
+ re.compile(r"^\d{2}:\d{2}:\d{2}\.\d{3}"), # HH:MM:SS.mmm
35
+ re.compile(r"^\[?\d{4}-\d{2}-\d{2}\]?"), # [YYYY-MM-DD]
36
+ re.compile(r"^\w+\s+\d+\s+\d{2}:\d{2}:\d{2}"), # syslog style
37
+ re.compile(r"^(ERROR|WARN|INFO|DEBUG|TRACE|FATAL)\b", re.IGNORECASE),
38
+ ]
39
+ HTML_PATTERNS: ClassVar[list[re.Pattern]] = [
40
+ re.compile(r"<html[\s>]", re.IGNORECASE),
41
+ re.compile(r"<!DOCTYPE\s+html", re.IGNORECASE),
42
+ re.compile(r"<head[\s>]", re.IGNORECASE),
43
+ re.compile(r"<body[\s>]", re.IGNORECASE),
44
+ re.compile(r"<div[\s>]", re.IGNORECASE),
45
+ re.compile(
46
+ r"</?(?:html|head|body|div|span|p|a|table|tr|td)[\s>]", re.IGNORECASE
47
+ ),
48
+ ]
49
+ CODE_KEYWORDS: ClassVar[set[str]] = {
50
+ "def",
51
+ "class",
52
+ "import",
53
+ "from",
54
+ "function",
55
+ "const",
56
+ "let",
57
+ "var",
58
+ "return",
59
+ "if",
60
+ "else",
61
+ "for",
62
+ "while",
63
+ "try",
64
+ "except",
65
+ "catch",
66
+ "public",
67
+ "private",
68
+ "protected",
69
+ "static",
70
+ "void",
71
+ "int",
72
+ "string",
73
+ "package",
74
+ "export",
75
+ "require",
76
+ "module",
77
+ }
78
+ SEARCH_PATTERNS: ClassVar[list[re.Pattern]] = [
79
+ re.compile(r"^Found\s+\d+\s+results?", re.IGNORECASE),
80
+ re.compile(r"^\d+\s+matches?", re.IGNORECASE),
81
+ re.compile(r"^---\s+search\s+results?\s+---", re.IGNORECASE),
82
+ re.compile(r"^\d+:\d+:.*\x1b", re.MULTILINE), # grep -n output
83
+ ]
84
+
85
+ @classmethod
86
+ def detect(cls, stdout: str) -> ContentType:
87
+ """Detect content type from stdout string.
88
+
89
+ Args:
90
+ stdout: The full stdout from the shell command.
91
+
92
+ Returns:
93
+ ContentType enum value.
94
+ """
95
+ if not stdout or not stdout.strip():
96
+ return ContentType.UNKNOWN
97
+
98
+ text = stdout.strip()
99
+
100
+ # 1. JSON detection — try to parse
101
+ if cls._is_json(text):
102
+ return ContentType.JSON
103
+
104
+ # 2. Diff detection — unified diff headers
105
+ if cls.DIFF_HEADER.search(text):
106
+ return ContentType.DIFF
107
+
108
+ # 3. Log detection — timestamp patterns or log levels
109
+ if cls._is_log(text):
110
+ return ContentType.LOG
111
+
112
+ # 4. HTML detection
113
+ if cls._is_html(text):
114
+ return ContentType.HTML
115
+
116
+ # 5. Search results detection
117
+ if cls._is_search(text):
118
+ return ContentType.SEARCH
119
+
120
+ # 6. Code detection — keyword density
121
+ if cls._is_code(text):
122
+ return ContentType.CODE
123
+
124
+ return ContentType.UNKNOWN
125
+
126
+ # ------------------------------------------------------------------
127
+ # Private heuristics
128
+ # ------------------------------------------------------------------
129
+
130
+ @classmethod
131
+ def _is_json(cls, text: str) -> bool:
132
+ """Test if text is valid JSON (object or array)."""
133
+ # Fast check: first non-whitespace char is { or [
134
+ first = text.lstrip()[0] if text else ""
135
+ if first not in ("{", "["):
136
+ return False
137
+ try:
138
+ json.loads(text)
139
+ return True
140
+ except ValueError:
141
+ return False
142
+
143
+ @classmethod
144
+ def _is_log(cls, text: str) -> bool:
145
+ """Test if text looks like log output."""
146
+ lines = text.splitlines()
147
+ if not lines:
148
+ return False
149
+ # Count lines matching log patterns
150
+ log_lines = 0
151
+ for line in lines[:50]: # sample first 50 lines
152
+ for pat in cls.LOG_PATTERNS:
153
+ if pat.search(line):
154
+ log_lines += 1
155
+ break
156
+ # If > 30% of sampled lines match, treat as log
157
+ return log_lines > max(1, min(len(lines), 50) * 0.3)
158
+
159
+ @classmethod
160
+ def _is_html(cls, text: str) -> bool:
161
+ """Test if text looks like HTML."""
162
+ return any(pat.search(text[:2000]) for pat in cls.HTML_PATTERNS)
163
+
164
+ @classmethod
165
+ def _is_code(cls, text: str) -> bool:
166
+ """Test if text looks like source code by keyword density."""
167
+ lines = text.splitlines()
168
+ if not lines:
169
+ return False
170
+ words = text.lower().split()
171
+ if not words:
172
+ return False
173
+ code_words = sum(1 for w in words[:500] if w in cls.CODE_KEYWORDS)
174
+ # If > 5% of words are code keywords, treat as code
175
+ return code_words > max(3, min(len(words), 500) * 0.05)
176
+
177
+ @classmethod
178
+ def _is_search(cls, text: str) -> bool:
179
+ """Test if text looks like search results (grep/rg/find output)."""
180
+ for pat in cls.SEARCH_PATTERNS:
181
+ if pat.search(text[:2000]):
182
+ return True
183
+ # Also check for filename:line: pattern (grep -n)
184
+ return re.search(r"^\S+\.\w+:\d+:", text[:2000], re.MULTILINE) is not None
@@ -0,0 +1,244 @@
1
+ """Filter dispatcher for the filter engine.
2
+
3
+ Orchestrates classification → strategy lookup → execution → compression.
4
+ """
5
+
6
+ import logging
7
+ import re
8
+ from datetime import UTC, datetime
9
+ from pathlib import Path
10
+ from typing import Any
11
+
12
+ from code_muse.plugins.filter_engine.classifier import CommandClassifier
13
+ from code_muse.plugins.filter_engine.registry import get_registry
14
+ from code_muse.plugins.filter_engine.verbosity import get_verbosity
15
+ from code_muse.tools.command_runner import ShellCommandOutput, _execute_shell_command
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ class FilterDispatcher:
21
+ """Singleton dispatcher that handles the full filter pipeline."""
22
+
23
+ _instance: FilterDispatcher | None = None
24
+
25
+ def __init__(self) -> None:
26
+ """Initialise the dispatcher with its classifier."""
27
+ self.classifier = CommandClassifier()
28
+
29
+ @classmethod
30
+ def get_instance(cls) -> FilterDispatcher:
31
+ """Return the module-level singleton."""
32
+ if cls._instance is None:
33
+ cls._instance = cls()
34
+ return cls._instance
35
+
36
+ @staticmethod
37
+ def _normalize_command(command: str) -> str:
38
+ """Rewrite plain ``git status`` to force porcelain format.
39
+
40
+ Appends ``--porcelain -b`` when the command is ``git status`` (or
41
+ variants like ``git -C <dir> status``) and does not already request
42
+ porcelain or short output.
43
+
44
+ Args:
45
+ command: The raw shell command.
46
+
47
+ Returns:
48
+ The original command, or a rewritten version with porcelain flags.
49
+ """
50
+ stripped = command.strip()
51
+ # Must contain git + status as separate words
52
+ if not re.search(r"\bgit\b.*\bstatus\b", stripped):
53
+ return command
54
+
55
+ tokens = stripped.split()
56
+ # Already porcelain/short — leave alone
57
+ if any(t in {"--porcelain", "--short", "-s"} for t in tokens):
58
+ return command
59
+
60
+ return f"{stripped} --porcelain -b"
61
+
62
+ async def handle(
63
+ self,
64
+ context: Any,
65
+ command: str,
66
+ cwd: str | None,
67
+ timeout: int,
68
+ ) -> dict[str, Any] | None:
69
+ """Run the full filter pipeline for a shell command.
70
+
71
+ Steps:
72
+
73
+ 1. Classify the command.
74
+ 2. Look up the strategy for the category.
75
+ 3. If the strategy is ``None`` or the ``unknown`` passthrough,
76
+ return ``None`` so normal execution proceeds.
77
+ 4. Execute the command via :func:`_execute_shell_command`.
78
+ 5. Apply the strategy to the resulting :class:`ShellCommandOutput`.
79
+ 6. Return ``{"pre_executed": True, "output": filtered_output}``.
80
+
81
+ On any strategy exception the error is logged and ``None`` is returned
82
+ so the raw output is preserved as a fallback.
83
+
84
+ Args:
85
+ context: pydantic-ai RunContext (unused).
86
+ command: The shell command to execute.
87
+ cwd: Working directory.
88
+ timeout: Timeout in seconds.
89
+
90
+ Returns:
91
+ A pre-executed result dict or ``None`` for passthrough.
92
+ """
93
+ category = self.classifier.classify(command)
94
+ registry = get_registry()
95
+
96
+ # Passthrough strategies return None
97
+ if category == "unknown":
98
+ return None
99
+
100
+ verbosity = get_verbosity()
101
+
102
+ # If verbosity is RAW, skip filtering entirely
103
+ if verbosity.value >= 4:
104
+ return None
105
+
106
+ try:
107
+ # Execute the command (sub-agents run silently)
108
+ from code_muse.tools.subagent_context import is_subagent
109
+
110
+ silent = is_subagent()
111
+ group_id = f"filter_engine_{id(command)}"
112
+
113
+ # Force porcelain for plain git status commands so the strategy
114
+ # parser always receives machine-readable output.
115
+ effective_command = self._normalize_command(command)
116
+ if effective_command != command:
117
+ logger.debug(
118
+ "FilterDispatcher: rewritten %r → %r", command, effective_command
119
+ )
120
+
121
+ # _execute_shell_command is async, but we are already in async context
122
+ output = await _execute_shell_command(
123
+ command=effective_command,
124
+ cwd=cwd,
125
+ timeout=timeout,
126
+ group_id=group_id,
127
+ silent=silent,
128
+ )
129
+
130
+ # -----------------------------------------------------------------
131
+ # Content-type routing (Epic 019)
132
+ # -----------------------------------------------------------------
133
+ from code_muse.plugins.filter_engine.content_detector import (
134
+ ContentType,
135
+ ContentTypeDetector,
136
+ )
137
+
138
+ content_type = ContentTypeDetector.detect(output.stdout or "")
139
+ logger.debug(
140
+ "Detected content type: %s for command: %s", content_type.value, command
141
+ )
142
+
143
+ content_strategy_map = {
144
+ ContentType.JSON: "json",
145
+ ContentType.DIFF: "diff",
146
+ ContentType.LOG: "log",
147
+ ContentType.HTML: "html",
148
+ ContentType.SEARCH: "search",
149
+ ContentType.CODE: category,
150
+ ContentType.UNKNOWN: category,
151
+ }
152
+
153
+ effective_category = content_strategy_map.get(content_type, category)
154
+
155
+ strategy = registry.get_strategy(effective_category)
156
+ if strategy is None and effective_category != category:
157
+ logger.debug(
158
+ "No strategy for %s, falling back to command category %s",
159
+ effective_category,
160
+ category,
161
+ )
162
+ effective_category = category
163
+ strategy = registry.get_strategy(effective_category)
164
+
165
+ if strategy is None:
166
+ return None
167
+
168
+ # Apply strategy
169
+ filtered = strategy(
170
+ command,
171
+ output.stdout or "",
172
+ output.stderr or "",
173
+ output.exit_code or 0,
174
+ verbosity,
175
+ )
176
+
177
+ if filtered is None:
178
+ return None
179
+
180
+ # Track execution metrics (best-effort, never blocks)
181
+ try:
182
+ from code_muse.plugins.token_tracking.record import record_command
183
+
184
+ record_command(
185
+ command=command,
186
+ raw_stdout=output.stdout or "",
187
+ raw_stderr=output.stderr or "",
188
+ compressed_stdout=filtered.stdout or "",
189
+ compressed_stderr=filtered.stderr or "",
190
+ category=effective_category,
191
+ strategy=strategy.__name__
192
+ if hasattr(strategy, "__name__")
193
+ else str(strategy),
194
+ exit_code=output.exit_code or 0,
195
+ )
196
+ except Exception:
197
+ pass
198
+
199
+ return {"pre_executed": True, "output": filtered}
200
+ except Exception:
201
+ logger.exception("FilterDispatcher: strategy failed for %r", command)
202
+ # Tee recovery: save raw output so user can recover
203
+ try:
204
+ import tempfile
205
+
206
+ raw_stdout = (
207
+ getattr(locals().get("output"), "stdout", "(unavailable)")
208
+ or "(empty)"
209
+ )
210
+ raw_stderr = (
211
+ getattr(locals().get("output"), "stderr", "(unavailable)")
212
+ or "(empty)"
213
+ )
214
+
215
+ tee_dir = Path(tempfile.gettempdir()) / "muse_tee"
216
+ tee_dir.mkdir(exist_ok=True)
217
+ tee_path = (
218
+ tee_dir
219
+ / f"tee_{datetime.now(UTC).strftime('%Y%m%d_%H%M%S')}_{hash(command) & 0xFFFF:04x}.txt"
220
+ )
221
+ tee_path.write_text(
222
+ f"# Raw output saved after filter error\n"
223
+ f"# Command: {command}\n"
224
+ f"# Timestamp: {datetime.now(UTC).isoformat()}\n\n"
225
+ f"STDOUT:\n{raw_stdout}\n\n"
226
+ f"STDERR:\n{raw_stderr}\n"
227
+ )
228
+ tee_path.chmod(0o600) # user-only readable
229
+ logger.info("Tee recovery: raw output saved to %s", tee_path)
230
+ # Return hint that includes the tee path
231
+ return {
232
+ "pre_executed": True,
233
+ "output": ShellCommandOutput(
234
+ success=False,
235
+ command=command,
236
+ stdout=f"⚠ Filter error — raw output saved to {tee_path}",
237
+ stderr="",
238
+ exit_code=-1,
239
+ execution_time=0.0,
240
+ ),
241
+ }
242
+ except Exception as tee_exc:
243
+ logger.error("Tee recovery itself failed: %s", tee_exc)
244
+ return None
@@ -0,0 +1,188 @@
1
+ """Filter Engine plugin for Muse — callback registration.
2
+
3
+ Registers:
4
+ - ``run_shell_command`` callback that intercepts shell commands
5
+ - ``/init`` custom command for one-command project setup
6
+ - Startup hook for tee-file cleanup
7
+ """
8
+
9
+ import logging
10
+ import tempfile
11
+ import time
12
+ from pathlib import Path
13
+ from typing import Any
14
+
15
+ from code_muse.callbacks import register_callback
16
+ from code_muse.messaging import emit_info, emit_success, emit_warning
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+
21
+ # ---------------------------------------------------------------------------
22
+ # Cython JIT hooks — safe fallback to pure Python
23
+ # ---------------------------------------------------------------------------
24
+
25
+
26
+ def _setup_cython_hooks() -> None:
27
+ """Enable pyximport so .pyx modules compile on-the-fly.
28
+
29
+ Falls back gracefully when Cython is missing or compilation fails.
30
+ """
31
+ try:
32
+ import pyximport
33
+
34
+ pyximport.install(
35
+ language_level=3,
36
+ build_in_temp=True,
37
+ inplace=True,
38
+ )
39
+ import code_muse
40
+
41
+ if code_muse.CYTHON_ENABLED:
42
+ emit_success(
43
+ f"✅ Cython enabled — {code_muse.PYX_MODULE_COUNT} modules compiled"
44
+ )
45
+ else:
46
+ emit_warning("⚠️ Cython not available — running in pure Python mode")
47
+ except ImportError:
48
+ emit_warning("⚠️ Cython not available — running in pure Python mode")
49
+ except Exception: # noqa: BLE001
50
+ emit_warning("⚠️ Cython not available — running in pure Python mode")
51
+
52
+
53
+ # ---------------------------------------------------------------------------
54
+ # Startup hook — tee file cleanup
55
+ # ---------------------------------------------------------------------------
56
+
57
+
58
+ def _on_startup() -> None:
59
+ """Delete tee files older than 24 hours on startup."""
60
+ try:
61
+ tee_dir = Path(tempfile.gettempdir()) / "muse_tee"
62
+ if tee_dir.exists():
63
+ now = time.time()
64
+ for f in tee_dir.iterdir():
65
+ if f.is_file() and now - f.stat().st_mtime > 86400:
66
+ f.unlink(missing_ok=True)
67
+ except Exception:
68
+ pass
69
+
70
+
71
+ # ---------------------------------------------------------------------------
72
+ # Run-shell-command callback
73
+ # ---------------------------------------------------------------------------
74
+
75
+
76
+ async def filter_engine_callback(
77
+ context: Any,
78
+ command: str,
79
+ cwd: str | None = None,
80
+ timeout: int = 60,
81
+ ) -> dict[str, Any] | None:
82
+ """Run-shell-command callback for the filter engine.
83
+
84
+ Args:
85
+ context: The pydantic-ai RunContext (unused).
86
+ command: The shell command to potentially intercept.
87
+ cwd: Working directory for the command.
88
+ timeout: Timeout in seconds.
89
+
90
+ Returns:
91
+ ``{"pre_executed": True, "output": ShellCommandOutput(...)}`` when the
92
+ filter engine handles the command, or ``None`` to let normal execution
93
+ proceed.
94
+ """
95
+ from code_muse.plugins.filter_engine.dispatcher import FilterDispatcher
96
+
97
+ dispatcher = FilterDispatcher.get_instance()
98
+ return await dispatcher.handle(context, command, cwd, timeout)
99
+
100
+
101
+ # ---------------------------------------------------------------------------
102
+ # /init custom command
103
+ # ---------------------------------------------------------------------------
104
+
105
+
106
+ _INIT_MARKDOWN = """# Fast-Puppy Token Saving
107
+
108
+ This project uses Fast-Puppy to compress shell command output, reducing token usage by 60-90%.
109
+
110
+ ## Enabled Strategies
111
+ - Git output compression (status, log, diff)
112
+ - Test runner failure focus
113
+ - Lint output grouping
114
+ - Code-aware read filtering
115
+
116
+ ## Configuration
117
+ See `~/.muse/config.toml` for global settings.
118
+
119
+ ## Commands
120
+ Run `/tracking gain` to see token savings.
121
+ """
122
+
123
+ _PROJECT_FILES = [
124
+ "pyproject.toml",
125
+ "package.json",
126
+ "Cargo.toml",
127
+ "go.mod",
128
+ "Gemfile",
129
+ ]
130
+
131
+
132
+ def _detect_project_type(cwd: Path) -> str | None:
133
+ """Detect project type from known manifest files."""
134
+ for manifest in _PROJECT_FILES:
135
+ if (cwd / manifest).exists():
136
+ return manifest
137
+ return None
138
+
139
+
140
+ def _on_custom_command(command: str, name: str) -> bool | None: # noqa: ARG001
141
+ """Handle ``/init`` — one-command Fast-Puppy setup."""
142
+ if name != "init":
143
+ return None
144
+
145
+ cwd = Path.cwd()
146
+ manifest = _detect_project_type(cwd)
147
+
148
+ md_path = cwd / "FAST_PUPPY.md"
149
+ if md_path.exists():
150
+ emit_info("FAST_PUPPY.md already exists — nothing to do.")
151
+ return True
152
+
153
+ md_path.write_text(_INIT_MARKDOWN, encoding="utf-8")
154
+ if manifest:
155
+ emit_success(
156
+ f"✅ Fast-Puppy initialized. Detected {manifest}. "
157
+ f"Created FAST_PUPPY.md. Filter engine is active."
158
+ )
159
+ else:
160
+ emit_success(
161
+ "✅ Fast-Puppy initialized. Created FAST_PUPPY.md. Filter engine is active."
162
+ )
163
+ return True
164
+
165
+
166
+ # ---------------------------------------------------------------------------
167
+ # Help entries
168
+ # ---------------------------------------------------------------------------
169
+
170
+
171
+ def _on_custom_command_help() -> list[tuple[str, str]]:
172
+ return [
173
+ ("/init", "Initialize Fast-Puppy in the current project"),
174
+ ]
175
+
176
+
177
+ # ---------------------------------------------------------------------------
178
+ # Register all callbacks
179
+ # ---------------------------------------------------------------------------
180
+
181
+ register_callback("startup", _on_startup)
182
+ register_callback("startup", _setup_cython_hooks)
183
+ # Priority: runs first (before policy_engine=50). Performs content-type detection and routing.
184
+ register_callback("run_shell_command", filter_engine_callback, priority=10)
185
+ register_callback("custom_command", _on_custom_command)
186
+ register_callback("custom_command_help", _on_custom_command_help)
187
+
188
+ logger.debug("Filter Engine plugin callbacks registered")