alysis-code 0.13.0__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 (458) hide show
  1. alysis_code/__init__.py +3 -0
  2. alysis_code/__main__.py +4 -0
  3. alysis_code/_build_info.py +14 -0
  4. alysis_code/account_login.py +468 -0
  5. alysis_code/agent/README.md +35 -0
  6. alysis_code/agent/__init__.py +11 -0
  7. alysis_code/agent/acceptance_contract.py +2217 -0
  8. alysis_code/agent/blast_radius.py +1403 -0
  9. alysis_code/agent/cache_keepalive.py +227 -0
  10. alysis_code/agent/completion_certificate.py +366 -0
  11. alysis_code/agent/completion_gate.py +306 -0
  12. alysis_code/agent/empty_response_stall.py +403 -0
  13. alysis_code/agent/errors.py +28 -0
  14. alysis_code/agent/llm_calls.py +475 -0
  15. alysis_code/agent/mutation_classification.py +227 -0
  16. alysis_code/agent/prompt_context.py +2508 -0
  17. alysis_code/agent/read_ledger.py +253 -0
  18. alysis_code/agent/regression_baseline.py +642 -0
  19. alysis_code/agent/reproduction_first.py +610 -0
  20. alysis_code/agent/sensitive_output.py +629 -0
  21. alysis_code/agent/session.py +3218 -0
  22. alysis_code/agent/steering.py +191 -0
  23. alysis_code/agent/subagent_execution.py +5177 -0
  24. alysis_code/agent/subagent_workspace.py +666 -0
  25. alysis_code/agent/tools_assembly.py +4728 -0
  26. alysis_code/agent/turn/__init__.py +101 -0
  27. alysis_code/agent/turn/core.py +8483 -0
  28. alysis_code/agent/turn/events.py +113 -0
  29. alysis_code/agent/turn/exploration.py +590 -0
  30. alysis_code/agent/turn/interventions.py +65 -0
  31. alysis_code/agent/turn/read_cache.py +420 -0
  32. alysis_code/agent/turn/snapshot.py +179 -0
  33. alysis_code/agent/turn_contract.py +661 -0
  34. alysis_code/agent/turn_path.py +129 -0
  35. alysis_code/agent/verification.py +2885 -0
  36. alysis_code/agent/verification_commands.py +512 -0
  37. alysis_code/agent/verification_evidence.py +738 -0
  38. alysis_code/agent_loop.py +693 -0
  39. alysis_code/agent_runtimes/__init__.py +51 -0
  40. alysis_code/agent_runtimes/base.py +114 -0
  41. alysis_code/agent_runtimes/builtins.py +129 -0
  42. alysis_code/agent_runtimes/codex_cli.py +664 -0
  43. alysis_code/agent_runtimes/host.py +263 -0
  44. alysis_code/agent_runtimes/registry.py +64 -0
  45. alysis_code/agent_runtimes/service.py +150 -0
  46. alysis_code/agentbox_client.py +416 -0
  47. alysis_code/agentbox_integration.py +310 -0
  48. alysis_code/alysis_cloud.py +152 -0
  49. alysis_code/approval_scope.py +276 -0
  50. alysis_code/assets/README.md +33 -0
  51. alysis_code/assets/__init__.py +126 -0
  52. alysis_code/assets/asset_read_core.py +281 -0
  53. alysis_code/assets/budget_allocator.py +456 -0
  54. alysis_code/assets/comprehender.py +759 -0
  55. alysis_code/assets/index.py +654 -0
  56. alysis_code/assets/ingestion.py +275 -0
  57. alysis_code/assets/legacy_migration.py +413 -0
  58. alysis_code/assets/models.py +263 -0
  59. alysis_code/assets/ocr.py +239 -0
  60. alysis_code/assets/owl/ascii/f-000.txt +13 -0
  61. alysis_code/assets/owl/ascii/f-001.txt +13 -0
  62. alysis_code/assets/owl/ascii/f-002.txt +13 -0
  63. alysis_code/assets/owl/ascii/f-003.txt +13 -0
  64. alysis_code/assets/owl/ascii/f-004.txt +13 -0
  65. alysis_code/assets/owl/ascii/f-005.txt +13 -0
  66. alysis_code/assets/owl/ascii/f-006.txt +13 -0
  67. alysis_code/assets/owl/ascii/f-007.txt +13 -0
  68. alysis_code/assets/owl/ascii/f-008.txt +13 -0
  69. alysis_code/assets/owl/ascii/f-009.txt +13 -0
  70. alysis_code/assets/owl/ascii/f-010.txt +13 -0
  71. alysis_code/assets/owl/ascii/f-011.txt +13 -0
  72. alysis_code/assets/owl/ascii/f-012.txt +13 -0
  73. alysis_code/assets/owl/ascii/f-013.txt +13 -0
  74. alysis_code/assets/owl/ascii/f-014.txt +13 -0
  75. alysis_code/assets/owl/ascii/f-015.txt +13 -0
  76. alysis_code/assets/owl/ascii/f-016.txt +13 -0
  77. alysis_code/assets/owl/ascii/f-017.txt +13 -0
  78. alysis_code/assets/owl/ascii/f-018.txt +13 -0
  79. alysis_code/assets/owl/ascii/f-019.txt +13 -0
  80. alysis_code/assets/owl/ascii/f-020.txt +13 -0
  81. alysis_code/assets/owl/index.html +98 -0
  82. alysis_code/assets/owl/show-owl.sh +761 -0
  83. alysis_code/assets/paths.py +49 -0
  84. alysis_code/assets/plan_binding.py +326 -0
  85. alysis_code/assets/planner_context.py +466 -0
  86. alysis_code/assets/planner_tools.py +184 -0
  87. alysis_code/assets/prompts.py +101 -0
  88. alysis_code/assets/replanner_context.py +239 -0
  89. alysis_code/assets/surface.py +521 -0
  90. alysis_code/assets/untrusted_content.py +48 -0
  91. alysis_code/assets/usage_logger.py +94 -0
  92. alysis_code/assets/worker_mirror.py +428 -0
  93. alysis_code/assets/worker_section.py +303 -0
  94. alysis_code/assets/worker_tools.py +468 -0
  95. alysis_code/atomic_io.py +83 -0
  96. alysis_code/auth_diagnostics.py +272 -0
  97. alysis_code/background_runner.py +366 -0
  98. alysis_code/branding.py +270 -0
  99. alysis_code/budget_policy.py +390 -0
  100. alysis_code/build_identity.py +465 -0
  101. alysis_code/builtin_hooks/__init__.py +7 -0
  102. alysis_code/builtin_hooks/notify_done_windows.py +65 -0
  103. alysis_code/bwrap_etc.py +76 -0
  104. alysis_code/cancellation.py +41 -0
  105. alysis_code/capabilities.py +137 -0
  106. alysis_code/chatgpt_codex_static_provider.py +133 -0
  107. alysis_code/cli.py +51 -0
  108. alysis_code/cli_impl/__init__.py +1 -0
  109. alysis_code/cli_impl/assets_cli.py +537 -0
  110. alysis_code/cli_impl/assets_modal.py +412 -0
  111. alysis_code/cli_impl/chat/__init__.py +156 -0
  112. alysis_code/cli_impl/chat/commands.py +2616 -0
  113. alysis_code/cli_impl/chat/loop.py +4508 -0
  114. alysis_code/cli_impl/chat/mid_turn_policy.py +125 -0
  115. alysis_code/cli_impl/chat/rendering.py +444 -0
  116. alysis_code/cli_impl/chat/state.py +124 -0
  117. alysis_code/cli_impl/chat_resume.py +830 -0
  118. alysis_code/cli_impl/chat_slash_completer.py +258 -0
  119. alysis_code/cli_impl/commands/__init__.py +11 -0
  120. alysis_code/cli_impl/commands/_shared.py +89 -0
  121. alysis_code/cli_impl/commands/auth.py +623 -0
  122. alysis_code/cli_impl/commands/chat_resume_helpers.py +1531 -0
  123. alysis_code/cli_impl/commands/chat_state.py +158 -0
  124. alysis_code/cli_impl/commands/chat_status.py +1248 -0
  125. alysis_code/cli_impl/commands/chat_terminal.py +942 -0
  126. alysis_code/cli_impl/commands/chat_tui_panels.py +1018 -0
  127. alysis_code/cli_impl/commands/cli_common.py +1223 -0
  128. alysis_code/cli_impl/commands/cli_surface.py +77 -0
  129. alysis_code/cli_impl/commands/config.py +131 -0
  130. alysis_code/cli_impl/commands/conventions.py +85 -0
  131. alysis_code/cli_impl/commands/execution_helpers.py +350 -0
  132. alysis_code/cli_impl/commands/extensions.py +401 -0
  133. alysis_code/cli_impl/commands/forge.py +1282 -0
  134. alysis_code/cli_impl/commands/forge_asset_view.py +121 -0
  135. alysis_code/cli_impl/commands/forge_helpers.py +1215 -0
  136. alysis_code/cli_impl/commands/hooks.py +737 -0
  137. alysis_code/cli_impl/commands/ide_bridge.py +31 -0
  138. alysis_code/cli_impl/commands/mcp.py +700 -0
  139. alysis_code/cli_impl/commands/profile.py +453 -0
  140. alysis_code/cli_impl/commands/prompt_helpers.py +307 -0
  141. alysis_code/cli_impl/commands/report.py +88 -0
  142. alysis_code/cli_impl/commands/root.py +1118 -0
  143. alysis_code/cli_impl/commands/sandbox.py +184 -0
  144. alysis_code/cli_impl/commands/server.py +54 -0
  145. alysis_code/cli_impl/commands/sessions.py +252 -0
  146. alysis_code/cli_impl/commands/skills.py +404 -0
  147. alysis_code/cli_impl/commands/startup.py +946 -0
  148. alysis_code/cli_impl/commands/tools.py +335 -0
  149. alysis_code/cli_impl/commands/update.py +364 -0
  150. alysis_code/cli_impl/commands/welcome.py +972 -0
  151. alysis_code/cli_impl/config_menu.py +3882 -0
  152. alysis_code/cli_impl/forge.py +4509 -0
  153. alysis_code/cli_impl/forge_recovery.py +485 -0
  154. alysis_code/cli_impl/setup_wizard.py +2409 -0
  155. alysis_code/cli_impl/tui/__init__.py +58 -0
  156. alysis_code/cli_impl/tui/app.py +4551 -0
  157. alysis_code/cli_impl/tui/config.py +32 -0
  158. alysis_code/cli_impl/tui/config_flow.py +2754 -0
  159. alysis_code/cli_impl/tui/config_overlay.py +566 -0
  160. alysis_code/cli_impl/tui/content.py +78 -0
  161. alysis_code/cli_impl/tui/footer.py +218 -0
  162. alysis_code/cli_impl/tui/forge_status.py +136 -0
  163. alysis_code/cli_impl/tui/markdown.py +244 -0
  164. alysis_code/cli_impl/tui/owl.py +109 -0
  165. alysis_code/cli_impl/tui/plan_meta.py +477 -0
  166. alysis_code/cli_impl/tui/setup_app.py +519 -0
  167. alysis_code/cli_impl/tui/setup_flow.py +1622 -0
  168. alysis_code/cli_impl/tui/state.py +101 -0
  169. alysis_code/cli_impl/tui/subagent_identity.py +66 -0
  170. alysis_code/cli_impl/tui/subagent_panel.py +186 -0
  171. alysis_code/cli_impl/tui/surface.py +796 -0
  172. alysis_code/cli_impl/tui/transcript.py +514 -0
  173. alysis_code/cli_impl/tui/update_prompt.py +79 -0
  174. alysis_code/cli_impl/tui/workspace_guard.py +384 -0
  175. alysis_code/clipboard.py +172 -0
  176. alysis_code/code_review.py +1211 -0
  177. alysis_code/compaction/__init__.py +28 -0
  178. alysis_code/compaction/conversation_compactor.py +2932 -0
  179. alysis_code/compaction/importance.py +177 -0
  180. alysis_code/compaction/settings.py +297 -0
  181. alysis_code/compaction/tool_output_offload.py +447 -0
  182. alysis_code/config.py +3509 -0
  183. alysis_code/conflict_auto_resolver.py +895 -0
  184. alysis_code/context/__init__.py +1 -0
  185. alysis_code/context/tool_schema_budgeter.py +220 -0
  186. alysis_code/crash_diagnostics.py +282 -0
  187. alysis_code/custom_tools/README.md +34 -0
  188. alysis_code/custom_tools/__init__.py +43 -0
  189. alysis_code/custom_tools/discovery.py +903 -0
  190. alysis_code/custom_tools/runtime.py +1516 -0
  191. alysis_code/custom_tools/session.py +227 -0
  192. alysis_code/custom_tools/trust.py +232 -0
  193. alysis_code/diff_paths.py +113 -0
  194. alysis_code/direction_change.py +293 -0
  195. alysis_code/dispatch_timing.py +306 -0
  196. alysis_code/durable_service_manager.py +1236 -0
  197. alysis_code/edit_discipline.py +659 -0
  198. alysis_code/error_text.py +73 -0
  199. alysis_code/execution_budget.py +411 -0
  200. alysis_code/execution_context.py +915 -0
  201. alysis_code/execution_deadline.py +1065 -0
  202. alysis_code/execution_shared.py +1904 -0
  203. alysis_code/extensions/README.md +30 -0
  204. alysis_code/extensions/__init__.py +93 -0
  205. alysis_code/extensions/activation.py +138 -0
  206. alysis_code/extensions/install.py +1436 -0
  207. alysis_code/extensions/manifest.py +487 -0
  208. alysis_code/extensions/models.py +74 -0
  209. alysis_code/extensions/paths.py +56 -0
  210. alysis_code/extensions/registry.json +4 -0
  211. alysis_code/extensions/registry.py +52 -0
  212. alysis_code/extensions/state.py +83 -0
  213. alysis_code/extensions/workspace_trust.py +101 -0
  214. alysis_code/failed_task_evidence.py +369 -0
  215. alysis_code/failure_category.py +315 -0
  216. alysis_code/feedback_report.py +1647 -0
  217. alysis_code/file_classification.py +485 -0
  218. alysis_code/forge.py +2064 -0
  219. alysis_code/forge_completion.py +362 -0
  220. alysis_code/forge_events.py +475 -0
  221. alysis_code/frontmatter_utils.py +95 -0
  222. alysis_code/git_evidence.py +1181 -0
  223. alysis_code/git_ops.py +560 -0
  224. alysis_code/git_safe.py +62 -0
  225. alysis_code/git_worktrees.py +190 -0
  226. alysis_code/hooks/README.md +33 -0
  227. alysis_code/hooks/__init__.py +67 -0
  228. alysis_code/hooks/audit.py +171 -0
  229. alysis_code/hooks/config.py +225 -0
  230. alysis_code/hooks/dispatcher.py +1110 -0
  231. alysis_code/hooks/models.py +447 -0
  232. alysis_code/hooks/trust.py +202 -0
  233. alysis_code/host_actions.py +543 -0
  234. alysis_code/host_browser.py +103 -0
  235. alysis_code/ide/__init__.py +5 -0
  236. alysis_code/ide/activity_events.py +399 -0
  237. alysis_code/ide/approvals.py +337 -0
  238. alysis_code/ide/artifacts.py +153 -0
  239. alysis_code/ide/browser_egress_proxy.py +1076 -0
  240. alysis_code/ide/cdp_websocket_transport.py +1192 -0
  241. alysis_code/ide/change_ledger.py +1721 -0
  242. alysis_code/ide/context_blocks.py +979 -0
  243. alysis_code/ide/event_stream.py +531 -0
  244. alysis_code/ide/forge_protocol.py +3112 -0
  245. alysis_code/ide/forge_request_ledger.py +737 -0
  246. alysis_code/ide/health.py +965 -0
  247. alysis_code/ide/managed_browser.py +2251 -0
  248. alysis_code/ide/management_protocol.py +3414 -0
  249. alysis_code/ide/mcp_oauth_coordinator.py +744 -0
  250. alysis_code/ide/mcp_oauth_lifecycle.py +1504 -0
  251. alysis_code/ide/prompt_queue.py +1070 -0
  252. alysis_code/ide/protocol.py +191 -0
  253. alysis_code/ide/resumable_swarm.py +1543 -0
  254. alysis_code/ide/session_search.py +295 -0
  255. alysis_code/ide/stdio_bridge.py +9935 -0
  256. alysis_code/ide/structured_state.py +1579 -0
  257. alysis_code/ide/swarm_protocol.py +816 -0
  258. alysis_code/integration_gate.py +506 -0
  259. alysis_code/interactive_input_guard.py +39 -0
  260. alysis_code/interactive_plan_mode.py +26 -0
  261. alysis_code/internal_artifacts.py +179 -0
  262. alysis_code/knowledge_base.py +1409 -0
  263. alysis_code/knowledge_capture.py +1190 -0
  264. alysis_code/knowledge_librarian.py +605 -0
  265. alysis_code/language_policy.py +34 -0
  266. alysis_code/litellm_static_provider.py +535 -0
  267. alysis_code/llm/__init__.py +1 -0
  268. alysis_code/llm/anthropic_messages.py +2288 -0
  269. alysis_code/llm/base.py +71 -0
  270. alysis_code/llm/cache_capabilities.py +985 -0
  271. alysis_code/llm/cache_control_blocks.py +244 -0
  272. alysis_code/llm/cache_policy.py +388 -0
  273. alysis_code/llm/factory.py +373 -0
  274. alysis_code/llm/gemini_generate_content.py +2652 -0
  275. alysis_code/llm/gemini_interactions.py +739 -0
  276. alysis_code/llm/metadata.py +450 -0
  277. alysis_code/llm/openai_compat.py +2947 -0
  278. alysis_code/llm/openai_responses.py +2604 -0
  279. alysis_code/llm/protocols.py +609 -0
  280. alysis_code/llm/provider_limits.py +525 -0
  281. alysis_code/llm/request_plan.py +389 -0
  282. alysis_code/llm/request_shape.py +238 -0
  283. alysis_code/llm/streaming.py +108 -0
  284. alysis_code/llm/temperature_compat.py +78 -0
  285. alysis_code/llm/types.py +195 -0
  286. alysis_code/llm/usage_normalization.py +222 -0
  287. alysis_code/llm_error_display.py +315 -0
  288. alysis_code/logging_redaction.py +326 -0
  289. alysis_code/managed_host_deadline.py +196 -0
  290. alysis_code/mcp/README.md +33 -0
  291. alysis_code/mcp/__init__.py +24 -0
  292. alysis_code/mcp/client.py +1137 -0
  293. alysis_code/mcp/config.py +597 -0
  294. alysis_code/mcp/errors.py +113 -0
  295. alysis_code/mcp/forge_scope.py +154 -0
  296. alysis_code/mcp/jsonrpc.py +214 -0
  297. alysis_code/mcp/manager.py +2308 -0
  298. alysis_code/mcp/models.py +666 -0
  299. alysis_code/mcp/oauth.py +972 -0
  300. alysis_code/mcp/oauth_runtime.py +310 -0
  301. alysis_code/mcp/oauth_store.py +276 -0
  302. alysis_code/mcp/prompts.py +329 -0
  303. alysis_code/mcp/resources.py +295 -0
  304. alysis_code/mcp/roots.py +106 -0
  305. alysis_code/mcp/server_requests.py +75 -0
  306. alysis_code/mcp/token_store.py +859 -0
  307. alysis_code/mcp/transport_http.py +1338 -0
  308. alysis_code/mcp/transport_stdio.py +1267 -0
  309. alysis_code/mcp/untrusted_content.py +119 -0
  310. alysis_code/merge_conflict_reviewer.py +729 -0
  311. alysis_code/model_catalog/__init__.py +1 -0
  312. alysis_code/model_catalog/chatgpt_codex_subscription_snapshot.json +186 -0
  313. alysis_code/model_catalog/litellm_model_prices_snapshot.json +44715 -0
  314. alysis_code/model_catalog/litellm_model_prices_snapshot.meta.json +17 -0
  315. alysis_code/model_metadata_policy.py +223 -0
  316. alysis_code/model_metadata_utils.py +103 -0
  317. alysis_code/model_registry.py +1420 -0
  318. alysis_code/model_router.py +147 -0
  319. alysis_code/permission_policy.py +1016 -0
  320. alysis_code/personas.py +451 -0
  321. alysis_code/pipeline_facts.py +233 -0
  322. alysis_code/plan_assistant.py +4763 -0
  323. alysis_code/plan_mode.py +393 -0
  324. alysis_code/plan_reconciliation.py +1228 -0
  325. alysis_code/plan_repair.py +652 -0
  326. alysis_code/plan_validation.py +1099 -0
  327. alysis_code/planning_constraints.py +904 -0
  328. alysis_code/policy.py +95 -0
  329. alysis_code/preview_server.py +457 -0
  330. alysis_code/process_reaping.py +566 -0
  331. alysis_code/profile_presets.py +1834 -0
  332. alysis_code/profiles.py +666 -0
  333. alysis_code/provider_auth/__init__.py +29 -0
  334. alysis_code/provider_auth/base.py +99 -0
  335. alysis_code/provider_auth/openai_codex.py +951 -0
  336. alysis_code/provider_auth/registry.py +76 -0
  337. alysis_code/provider_auth/store.py +125 -0
  338. alysis_code/provider_diagnostics.py +1209 -0
  339. alysis_code/provider_model_catalog.py +685 -0
  340. alysis_code/provider_telemetry.py +1699 -0
  341. alysis_code/provider_url.py +75 -0
  342. alysis_code/reasoning_contracts.py +911 -0
  343. alysis_code/remote_sync.py +350 -0
  344. alysis_code/replanning.py +1195 -0
  345. alysis_code/repo_scan.py +1152 -0
  346. alysis_code/request_estimation.py +296 -0
  347. alysis_code/review_gate.py +617 -0
  348. alysis_code/run_lock.py +1141 -0
  349. alysis_code/run_outcome.py +58 -0
  350. alysis_code/run_provenance.py +774 -0
  351. alysis_code/run_state.py +445 -0
  352. alysis_code/runtime_artifacts.py +116 -0
  353. alysis_code/runtime_context_features.py +78 -0
  354. alysis_code/runtime_kind.py +52 -0
  355. alysis_code/safety/__init__.py +11 -0
  356. alysis_code/safety/mcp_sanitize.py +29 -0
  357. alysis_code/safety/safe_http.py +297 -0
  358. alysis_code/safety/subagent_report.py +184 -0
  359. alysis_code/sandbox_doctor.py +682 -0
  360. alysis_code/sandbox_runner.py +1025 -0
  361. alysis_code/sandbox_settings.py +423 -0
  362. alysis_code/serialized_paths.py +355 -0
  363. alysis_code/server/__init__.py +3 -0
  364. alysis_code/server/app.py +367 -0
  365. alysis_code/server/auth.py +34 -0
  366. alysis_code/server/job_config.py +30 -0
  367. alysis_code/server/settings.py +215 -0
  368. alysis_code/server/store.py +193 -0
  369. alysis_code/server/worker_runner.py +657 -0
  370. alysis_code/service_persistence.py +355 -0
  371. alysis_code/session_artifacts.py +108 -0
  372. alysis_code/session_metrics.py +331 -0
  373. alysis_code/session_store.py +624 -0
  374. alysis_code/skills/README.md +34 -0
  375. alysis_code/skills/__init__.py +104 -0
  376. alysis_code/skills/conventions.py +84 -0
  377. alysis_code/skills/discovery.py +176 -0
  378. alysis_code/skills/eval_models.py +232 -0
  379. alysis_code/skills/eval_runner.py +372 -0
  380. alysis_code/skills/evals.py +1344 -0
  381. alysis_code/skills/install.py +293 -0
  382. alysis_code/skills/loader.py +118 -0
  383. alysis_code/skills/matching.py +103 -0
  384. alysis_code/skills/models.py +71 -0
  385. alysis_code/skills/paths.py +56 -0
  386. alysis_code/skills/prompting.py +500 -0
  387. alysis_code/skills/scaffold.py +142 -0
  388. alysis_code/skills/state.py +441 -0
  389. alysis_code/skills/transactions.py +125 -0
  390. alysis_code/skills/validation.py +304 -0
  391. alysis_code/step_budget.py +238 -0
  392. alysis_code/subagent_labels.py +49 -0
  393. alysis_code/subagents.py +1072 -0
  394. alysis_code/surface/__init__.py +80 -0
  395. alysis_code/surface/base.py +305 -0
  396. alysis_code/surface/console.py +387 -0
  397. alysis_code/surface/events.py +372 -0
  398. alysis_code/surface/hidden_surface.py +529 -0
  399. alysis_code/surface/noop_surface.py +219 -0
  400. alysis_code/surface/rich_surface.py +1555 -0
  401. alysis_code/surface/styles.py +67 -0
  402. alysis_code/surface/theme.py +455 -0
  403. alysis_code/surface/types.py +100 -0
  404. alysis_code/swarm_backend.py +926 -0
  405. alysis_code/swarm_orchestrator.py +4020 -0
  406. alysis_code/swarm_scheduler.py +441 -0
  407. alysis_code/swarm_trace.py +429 -0
  408. alysis_code/swarm_worker.py +2119 -0
  409. alysis_code/swarm_write_guard.py +348 -0
  410. alysis_code/task_dependencies.py +170 -0
  411. alysis_code/task_readiness.py +992 -0
  412. alysis_code/task_scope.py +2148 -0
  413. alysis_code/terminal_manager.py +762 -0
  414. alysis_code/terminal_ownership.py +460 -0
  415. alysis_code/text_normalization.py +30 -0
  416. alysis_code/token_budget.py +97 -0
  417. alysis_code/tools/README.md +34 -0
  418. alysis_code/tools/__init__.py +1 -0
  419. alysis_code/tools/artifacts.py +127 -0
  420. alysis_code/tools/availability.py +188 -0
  421. alysis_code/tools/fs.py +1456 -0
  422. alysis_code/tools/git.py +461 -0
  423. alysis_code/tools/history.py +229 -0
  424. alysis_code/tools/http_timeout.py +78 -0
  425. alysis_code/tools/image_generation.py +552 -0
  426. alysis_code/tools/registry.py +2936 -0
  427. alysis_code/tools/repo_map.py +476 -0
  428. alysis_code/tools/search.py +563 -0
  429. alysis_code/tools/shell.py +135 -0
  430. alysis_code/tools/symbols.py +1350 -0
  431. alysis_code/tools/test_discovery.py +643 -0
  432. alysis_code/tools/web.py +482 -0
  433. alysis_code/tools/web_search.py +2012 -0
  434. alysis_code/tools/web_search_dashscope.py +557 -0
  435. alysis_code/tools/web_search_ddgs.py +221 -0
  436. alysis_code/tools/web_search_provider_adapters.py +1429 -0
  437. alysis_code/tools/web_search_tavily.py +194 -0
  438. alysis_code/updates.py +933 -0
  439. alysis_code/usage_tracker.py +1990 -0
  440. alysis_code/verification_command_analysis.py +1004 -0
  441. alysis_code/verification_contract.py +574 -0
  442. alysis_code/verification_failure_summary.py +273 -0
  443. alysis_code/verification_repair.py +385 -0
  444. alysis_code/verify_gate.py +3129 -0
  445. alysis_code/web_research.py +1872 -0
  446. alysis_code/web_search_adapters.py +66 -0
  447. alysis_code/web_search_policy.py +27 -0
  448. alysis_code/workspace_binding.py +389 -0
  449. alysis_code/workspace_binding_ui.py +408 -0
  450. alysis_code/workspace_context.py +273 -0
  451. alysis_code/workspace_isolation.py +138 -0
  452. alysis_code/workspace_provisioning.py +455 -0
  453. alysis_code-0.13.0.dist-info/METADATA +507 -0
  454. alysis_code-0.13.0.dist-info/RECORD +458 -0
  455. alysis_code-0.13.0.dist-info/WHEEL +4 -0
  456. alysis_code-0.13.0.dist-info/entry_points.txt +3 -0
  457. alysis_code-0.13.0.dist-info/licenses/LICENSE +176 -0
  458. alysis_code-0.13.0.dist-info/licenses/NOTICE +4 -0
@@ -0,0 +1,412 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import sys
5
+ from pathlib import Path
6
+
7
+ import typer
8
+ from rich.console import Console
9
+ from rich.panel import Panel
10
+ from rich.table import Table
11
+
12
+ from ..assets import (
13
+ AssetAlreadyExistsError,
14
+ AssetError,
15
+ AssetSurface,
16
+ AssetSurfaceDetail,
17
+ )
18
+ from ..assets.ingestion import _classify_asset
19
+ from ..assets.surface import build_asset_surface
20
+ from ..branding import env_get
21
+ from ..config import AppConfig
22
+ from ..forge import RunPaths, load_plan
23
+ from ..surface.console import make_console
24
+
25
+ _STATUS_EMOJI = {
26
+ "ready": "🟢",
27
+ "pending": "🟡",
28
+ "running": "🟡",
29
+ "failed": "🔴",
30
+ "minimal": "⚪",
31
+ }
32
+ _STATUS_TEXT = {
33
+ "ready": "[ready]",
34
+ "pending": "[pending]",
35
+ "running": "[running]",
36
+ "failed": "[failed]",
37
+ "minimal": "[minimal]",
38
+ }
39
+
40
+
41
+ def run_assets_modal(
42
+ *,
43
+ cfg: AppConfig,
44
+ run_paths: RunPaths,
45
+ surface: AssetSurface | None = None,
46
+ console: Console | None = None,
47
+ ) -> None:
48
+ console = console or make_console()
49
+ if surface is None:
50
+ load_plan(run_paths)
51
+ surface = build_asset_surface(cfg=cfg, run_paths=run_paths)
52
+ if not sys.stdin.isatty():
53
+ console.print("Assets modal is unavailable when stdin is not a terminal.")
54
+ return
55
+ while True:
56
+ choice = _prompt_choice(
57
+ console=console,
58
+ title=f"Assets · run {run_paths.run_id}",
59
+ choices=[
60
+ ("add", "Add a new asset"),
61
+ ("list", f"See all assets ({len(surface.list_assets())})"),
62
+ ("back", "Back"),
63
+ ],
64
+ )
65
+ if choice in {None, "back"}:
66
+ return
67
+ if choice == "add":
68
+ _add_asset_flow(console=console, surface=surface, run_paths=run_paths)
69
+ elif choice == "list":
70
+ _list_flow(console=console, surface=surface)
71
+
72
+
73
+ def status_label(status: str, *, pinned: bool = False, emoji: bool | None = None) -> str:
74
+ use_emoji = terminal_supports_emoji() if emoji is None else emoji
75
+ status_key = str(status or "pending").strip().lower()
76
+ if use_emoji:
77
+ prefix = "📌" if pinned else ""
78
+ return f"{prefix}{_STATUS_EMOJI.get(status_key, _STATUS_EMOJI['pending'])}"
79
+ prefix = "[pinned]" if pinned else ""
80
+ return f"{prefix}{_STATUS_TEXT.get(status_key, _STATUS_TEXT['pending'])}"
81
+
82
+
83
+ def terminal_supports_emoji() -> bool:
84
+ if env_get("ALYSIS_NO_EMOJI") in {"1", "true", "yes", "on"}:
85
+ return False
86
+ encoding = getattr(sys.stdout, "encoding", None) or "utf-8"
87
+ try:
88
+ "🟢📌".encode(encoding)
89
+ except UnicodeEncodeError:
90
+ return False
91
+ return True
92
+
93
+
94
+ def _add_asset_flow(*, console: Console, surface: AssetSurface, run_paths: RunPaths) -> None:
95
+ source = _prompt_path(console=console, root=run_paths.root)
96
+ if source is None:
97
+ return
98
+ title = _prompt_limited_text(console, "Title", required=True, max_chars=80)
99
+ if title is None:
100
+ return
101
+ description = _prompt_limited_text(console, "Description", required=False, max_chars=240)
102
+ if description is None:
103
+ return
104
+ pinned = typer.confirm("Pinned", default=False)
105
+ console.print(
106
+ Panel(
107
+ "\n".join(
108
+ [
109
+ f"Path: {source}",
110
+ f"Title: {title}",
111
+ f"Description: {description or '-'}",
112
+ f"Pinned: {'yes' if pinned else 'no'}",
113
+ ]
114
+ ),
115
+ title="Confirm Asset",
116
+ border_style="bright_black",
117
+ )
118
+ )
119
+ if not typer.confirm("Add asset", default=True):
120
+ console.print("Cancelled.")
121
+ return
122
+ try:
123
+ result = surface.add_asset(
124
+ source,
125
+ title=title,
126
+ description=description,
127
+ pinned=pinned,
128
+ added_by={"phase": "modal", "command": "/assets"},
129
+ comprehend="async",
130
+ dedupe_policy="reject",
131
+ )
132
+ except AssetAlreadyExistsError as exc:
133
+ _handle_collision(
134
+ console=console, surface=surface, source=source, existing_id=exc.existing_id
135
+ )
136
+ return
137
+ except AssetError as exc:
138
+ console.print(f"[red]{exc}[/red]")
139
+ return
140
+ console.print(f"Asset added: {result.record.id}")
141
+ if result.comprehension_handle is not None:
142
+ console.print("Comprehension started in background.")
143
+
144
+
145
+ def _handle_collision(
146
+ *,
147
+ console: Console,
148
+ surface: AssetSurface,
149
+ source: Path,
150
+ existing_id: str,
151
+ ) -> None:
152
+ existing = surface.index.get(existing_id, include_deleted=True)
153
+ choice = _prompt_choice(
154
+ console=console,
155
+ title="Duplicate Asset",
156
+ choices=[
157
+ ("use", f'Use existing asset ({existing.id} - "{existing.title}")'),
158
+ ("link", "Return existing asset"),
159
+ ("cancel", "Cancel"),
160
+ ],
161
+ )
162
+ if choice == "use":
163
+ console.print(f"Using existing asset: {existing.id}")
164
+ return
165
+ if choice != "link":
166
+ console.print("Cancelled.")
167
+ return
168
+ result = surface.add_asset(
169
+ source,
170
+ title=existing.title,
171
+ description=existing.description,
172
+ pinned=existing.pinned,
173
+ added_by={"phase": "modal", "command": "/assets"},
174
+ comprehend="async",
175
+ dedupe_policy="link",
176
+ )
177
+ console.print(f"Using existing asset: {result.record.id}")
178
+
179
+
180
+ def _list_flow(*, console: Console, surface: AssetSurface) -> None:
181
+ while True:
182
+ entries = surface.list_assets()
183
+ if not entries:
184
+ console.print("No assets attached.")
185
+ return
186
+ table = Table(title="Assets")
187
+ table.add_column("status")
188
+ table.add_column("kind")
189
+ table.add_column("id")
190
+ table.add_column("title")
191
+ table.add_column("size")
192
+ for entry in entries:
193
+ table.add_row(
194
+ status_label(entry.comprehension_status, pinned=entry.record.pinned),
195
+ "img" if entry.record.kind == "image" else "txt",
196
+ entry.record.id,
197
+ entry.record.title,
198
+ _format_size(entry.record.size_bytes),
199
+ )
200
+ console.print(table)
201
+ choice = _prompt_choice(
202
+ console=console,
203
+ title="Assets",
204
+ choices=[
205
+ *(
206
+ (entry.record.id, f"{entry.record.id} - {entry.record.title}")
207
+ for entry in entries
208
+ ),
209
+ ("refresh", "Refresh status"),
210
+ ("back", "Back"),
211
+ ],
212
+ )
213
+ if choice in {None, "back"}:
214
+ return
215
+ if choice == "refresh":
216
+ continue
217
+ _detail_flow(console=console, surface=surface, asset_id=choice)
218
+
219
+
220
+ def _detail_flow(*, console: Console, surface: AssetSurface, asset_id: str) -> None:
221
+ while True:
222
+ try:
223
+ detail = surface.show_asset(asset_id)
224
+ except AssetError as exc:
225
+ console.print(f"[red]{exc}[/red]")
226
+ return
227
+ console.print(_detail_panel(detail))
228
+ deleted = detail.record.deleted_at is not None
229
+ choices = [("back", "Back")]
230
+ if not deleted:
231
+ choices = [
232
+ ("edit", "Edit metadata"),
233
+ ("toggle", "Toggle pinned"),
234
+ ("refresh", "Refresh comprehension"),
235
+ ("text", "View extracted text"),
236
+ ("json", "View comprehension JSON"),
237
+ ("delete", "Delete"),
238
+ ("back", "Back"),
239
+ ]
240
+ choice = _prompt_choice(console=console, title=detail.record.id, choices=choices)
241
+ if choice in {None, "back"}:
242
+ return
243
+ if choice == "edit":
244
+ _edit_metadata(console=console, surface=surface, detail=detail)
245
+ elif choice == "toggle":
246
+ surface.edit_asset(detail.record.id, pinned=not detail.record.pinned)
247
+ elif choice == "refresh":
248
+ surface.refresh_comprehension(detail.record.id, mode="async")
249
+ console.print("Refresh started - status will update on next refresh.")
250
+ elif choice == "text":
251
+ console.print(detail.extracted_text_preview or "No extracted text available")
252
+ elif choice == "json":
253
+ payload = detail.comprehension.to_dict() if detail.comprehension is not None else {}
254
+ console.print(json.dumps(payload, indent=2, sort_keys=True, ensure_ascii=False))
255
+ elif choice == "delete" and typer.confirm(f"Delete {detail.record.id}?", default=False):
256
+ surface.delete_asset(detail.record.id)
257
+ return
258
+
259
+
260
+ def _detail_panel(detail: AssetSurfaceDetail) -> Panel:
261
+ record = detail.record
262
+ comprehension = detail.comprehension
263
+ status = "deleted" if record.deleted_at is not None else detail.comprehension_status
264
+ lines = [
265
+ f"Status: {status}",
266
+ f"Kind: {record.kind} · {_format_size(record.size_bytes)} · {record.mime}",
267
+ f"Original file: {record.original_filename}",
268
+ f"Pinned: {'yes' if record.pinned else 'no'}",
269
+ f"Versions: {', '.join(str(version) for version in detail.versions) or '-'}",
270
+ "",
271
+ "Description:",
272
+ f" {record.description or '-'}",
273
+ ]
274
+ if record.deleted_at is not None:
275
+ lines.insert(1, f"Deleted at: {record.deleted_at}")
276
+ if comprehension is not None:
277
+ lines.extend(
278
+ [
279
+ "",
280
+ f"Detected lang: {comprehension.detected_language or '-'}",
281
+ f"Source: {comprehension.source}",
282
+ "",
283
+ "Comprehension summary:",
284
+ f" {comprehension.data.semantic_summary or '-'}",
285
+ ]
286
+ )
287
+ if comprehension.data.stated_facts:
288
+ lines.append("")
289
+ lines.append("Stated facts:")
290
+ lines.extend(f" - {fact}" for fact in comprehension.data.stated_facts[:8])
291
+ return Panel("\n".join(lines), title=f'{record.id} - "{record.title}"')
292
+
293
+
294
+ def _edit_metadata(*, console: Console, surface: AssetSurface, detail: AssetSurfaceDetail) -> None:
295
+ title = _prompt_limited_text(
296
+ console,
297
+ "Title",
298
+ required=True,
299
+ max_chars=80,
300
+ default=detail.record.title,
301
+ )
302
+ if title is None:
303
+ return
304
+ description = _prompt_limited_text(
305
+ console,
306
+ "Description",
307
+ required=False,
308
+ max_chars=240,
309
+ default=detail.record.description,
310
+ )
311
+ if description is None:
312
+ return
313
+ surface.edit_asset(detail.record.id, title=title, description=description)
314
+ console.print(
315
+ "Title/description changed. Comprehension reflects previous metadata. "
316
+ "Use 'Refresh comprehension' to update."
317
+ )
318
+
319
+
320
+ def _prompt_path(*, console: Console, root: Path) -> Path | None:
321
+ try:
322
+ raw = typer.prompt("Path", default="", show_default=False)
323
+ except (EOFError, KeyboardInterrupt):
324
+ console.print("")
325
+ return None
326
+ if not str(raw).strip():
327
+ return None
328
+ candidate = Path(str(raw)).expanduser()
329
+ if not candidate.is_absolute():
330
+ candidate = root / candidate
331
+ candidate = candidate.resolve()
332
+ try:
333
+ candidate.relative_to(root.resolve())
334
+ except ValueError:
335
+ console.print("[red]Asset path must stay inside the workspace root.[/red]")
336
+ return None
337
+ try:
338
+ raw_bytes = candidate.read_bytes()
339
+ except OSError as exc:
340
+ console.print(f"[red]Cannot read asset file: {exc}[/red]")
341
+ return None
342
+ try:
343
+ _classify_asset(candidate, raw_bytes)
344
+ except AssetError as exc:
345
+ console.print(f"[red]{exc}[/red]")
346
+ return None
347
+ return candidate
348
+
349
+
350
+ def _prompt_limited_text(
351
+ console: Console,
352
+ label: str,
353
+ *,
354
+ required: bool,
355
+ max_chars: int,
356
+ default: str = "",
357
+ ) -> str | None:
358
+ while True:
359
+ try:
360
+ value = str(typer.prompt(label, default=default, show_default=bool(default))).strip()
361
+ except (EOFError, KeyboardInterrupt):
362
+ console.print("")
363
+ return None
364
+ if not value and required:
365
+ console.print(f"[red]{label} is required.[/red]")
366
+ continue
367
+ if len(value) > max_chars:
368
+ console.print(f"[red]{label} must be {max_chars} characters or fewer.[/red]")
369
+ continue
370
+ return value
371
+
372
+
373
+ def _prompt_choice(
374
+ *,
375
+ console: Console,
376
+ title: str,
377
+ choices: list[tuple[str, str]],
378
+ ) -> str | None:
379
+ error_message: str | None = None
380
+ while True:
381
+ console.print()
382
+ console.print(f"[bold]{title}[/bold]")
383
+ if error_message:
384
+ console.print(f"[red]{error_message}[/red]")
385
+ for index, (_value, label) in enumerate(choices, start=1):
386
+ console.print(f" {index}) {label}")
387
+ try:
388
+ raw = str(typer.prompt("Choice", default="", show_default=False)).strip()
389
+ except (EOFError, KeyboardInterrupt):
390
+ console.print("")
391
+ return None
392
+ if not raw:
393
+ return None
394
+ if raw.lower() in {"q", "quit", "cancel", "back"}:
395
+ return None
396
+ if raw.isdigit():
397
+ index = int(raw) - 1
398
+ if 0 <= index < len(choices):
399
+ return choices[index][0]
400
+ for value, label in choices:
401
+ if raw.lower() in {value.lower(), label.lower()}:
402
+ return value
403
+ error_message = "Unknown choice."
404
+
405
+
406
+ def _format_size(size_bytes: int) -> str:
407
+ size = float(size_bytes)
408
+ for unit in ("B", "KB", "MB", "GB"):
409
+ if size < 1024 or unit == "GB":
410
+ return f"{size:.0f} {unit}" if unit == "B" else f"{size:.1f} {unit}"
411
+ size /= 1024
412
+ return f"{size_bytes} B"
@@ -0,0 +1,156 @@
1
+ from __future__ import annotations
2
+
3
+ import importlib
4
+ from typing import Any
5
+
6
+ _PUBLIC_FACADE_NAMES = {
7
+ "_PUBLIC_LOOP_COPY_VALUES",
8
+ "_copy_loop_globals_to_public",
9
+ "_apply_chat_effective_mode",
10
+ "_apply_chat_persona",
11
+ "_handle_chat_command",
12
+ "_handle_chat_command_impl",
13
+ "_handle_forge_chat_command",
14
+ "_handle_forge_chat_command_impl",
15
+ "_run_plan_mode_approval_loop",
16
+ "_run_plan_mode_approval_loop_impl",
17
+ "_sync_cli_globals",
18
+ "_sync_loop_globals_from_public",
19
+ "chat_impl",
20
+ "run_impl",
21
+ }
22
+ _PUBLIC_LOOP_COPY_VALUES: dict[str, Any] = {}
23
+
24
+
25
+ def _loop_module() -> Any:
26
+ return importlib.import_module(f"{__name__}.loop")
27
+
28
+
29
+ def _commands_module() -> Any:
30
+ return importlib.import_module(f"{__name__}.commands")
31
+
32
+
33
+ def _copy_loop_globals_to_public(*, overwrite: bool = False) -> None:
34
+ module_globals = globals()
35
+ for name, value in _loop_module().__dict__.items():
36
+ if name.startswith("__") or name in _PUBLIC_FACADE_NAMES:
37
+ continue
38
+ if overwrite or name not in module_globals:
39
+ module_globals[name] = value
40
+ _PUBLIC_LOOP_COPY_VALUES[name] = value
41
+
42
+
43
+ def _public_global_overrides() -> dict[str, Any]:
44
+ loop = _loop_module()
45
+ loop_globals = loop.__dict__
46
+ overrides: dict[str, Any] = {}
47
+ for name, value in globals().items():
48
+ if name.startswith("__") or name in _PUBLIC_FACADE_NAMES:
49
+ continue
50
+ if name not in loop_globals or loop_globals[name] is value:
51
+ continue
52
+ if _PUBLIC_LOOP_COPY_VALUES.get(name) is value:
53
+ continue
54
+ if callable(value) and getattr(value, "__module__", None) == loop.__name__:
55
+ continue
56
+ if name in loop_globals:
57
+ overrides[name] = value
58
+ return overrides
59
+
60
+
61
+ def _sync_loop_globals_from_public() -> None:
62
+ loop_globals = _loop_module().__dict__
63
+ for name, value in globals().items():
64
+ if name.startswith("__") or name in _PUBLIC_FACADE_NAMES:
65
+ continue
66
+ if _PUBLIC_LOOP_COPY_VALUES.get(name) is value:
67
+ continue
68
+ loop_globals[name] = value
69
+
70
+
71
+ def _sync_cli_globals(cli_mod: Any) -> None:
72
+ public_overrides = _public_global_overrides()
73
+ loop = _loop_module()
74
+ loop._sync_cli_globals(cli_mod)
75
+ for name, value in public_overrides.items():
76
+ setattr(loop, name, value)
77
+ _copy_loop_globals_to_public(overwrite=True)
78
+
79
+
80
+ def _handle_chat_command(*args: Any, **kwargs: Any) -> Any:
81
+ _commands_module()._sync_command_globals(globals())
82
+ return _commands_module()._handle_chat_command(*args, **kwargs)
83
+
84
+
85
+ def _handle_forge_chat_command(*args: Any, **kwargs: Any) -> Any:
86
+ _commands_module()._sync_command_globals(globals())
87
+ return _commands_module()._handle_forge_chat_command(*args, **kwargs)
88
+
89
+
90
+ def _run_plan_mode_approval_loop(*args: Any, **kwargs: Any) -> Any:
91
+ _sync_loop_globals_from_public()
92
+ return _loop_module()._run_plan_mode_approval_loop(*args, **kwargs)
93
+
94
+
95
+ def _apply_chat_effective_mode(*args: Any, **kwargs: Any) -> Any:
96
+ _sync_loop_globals_from_public()
97
+ return _loop_module()._apply_chat_effective_mode(*args, **kwargs)
98
+
99
+
100
+ def _apply_chat_persona(*args: Any, **kwargs: Any) -> Any:
101
+ _sync_loop_globals_from_public()
102
+ return _loop_module()._apply_chat_persona(*args, **kwargs)
103
+
104
+
105
+ def _handle_chat_command_impl(cli_mod: Any, *args: Any, **kwargs: Any) -> Any:
106
+ _sync_cli_globals(cli_mod)
107
+ return _handle_chat_command(*args, **kwargs)
108
+
109
+
110
+ def _handle_forge_chat_command_impl(cli_mod: Any, *args: Any, **kwargs: Any) -> Any:
111
+ _sync_cli_globals(cli_mod)
112
+ return _handle_forge_chat_command(*args, **kwargs)
113
+
114
+
115
+ def _run_plan_mode_approval_loop_impl(cli_mod: Any, *args: Any, **kwargs: Any) -> Any:
116
+ _sync_cli_globals(cli_mod)
117
+ return _run_plan_mode_approval_loop(*args, **kwargs)
118
+
119
+
120
+ def _print_chat_context_impl(cli_mod: Any, *args: Any, **kwargs: Any) -> Any:
121
+ _sync_cli_globals(cli_mod)
122
+ _sync_loop_globals_from_public()
123
+ return _loop_module()._print_chat_context(*args, **kwargs)
124
+
125
+
126
+ def chat_impl(cli_mod: Any, *args: Any, **kwargs: Any) -> Any:
127
+ _sync_cli_globals(cli_mod)
128
+ _sync_loop_globals_from_public()
129
+ return _loop_module().chat(*args, **kwargs)
130
+
131
+
132
+ def run_impl(cli_mod: Any, *args: Any, **kwargs: Any) -> Any:
133
+ _sync_cli_globals(cli_mod)
134
+ _sync_loop_globals_from_public()
135
+ return _loop_module().run(*args, **kwargs)
136
+
137
+
138
+ def __getattr__(name: str) -> Any:
139
+ _copy_loop_globals_to_public()
140
+ if name in globals():
141
+ return globals()[name]
142
+ value = getattr(_loop_module(), name)
143
+ globals()[name] = value
144
+ return value
145
+
146
+
147
+ def _exported_names() -> set[str]:
148
+ _copy_loop_globals_to_public()
149
+ return {name for name in globals() if not name.startswith("__") or name == "__version__"}
150
+
151
+
152
+ def __dir__() -> list[str]:
153
+ return sorted(_exported_names())
154
+
155
+
156
+ __all__ = tuple(sorted(_exported_names()))