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,441 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+ from dataclasses import dataclass, field
5
+ from pathlib import PurePosixPath
6
+ from typing import Any
7
+
8
+ from .file_classification import is_code_implementation_path, is_test_path
9
+ from .task_dependencies import infer_ordered_predecessor_dependency
10
+ from .task_scope import (
11
+ is_explicit_repo_path_pattern,
12
+ normalize_claimed_scope_patterns,
13
+ normalize_repo_path_list,
14
+ )
15
+ from .verification_repair import TASK_STATUS_COMPLETED_UNVERIFIED
16
+
17
+ _EXPECTED_RED_REGRESSION_TEXT_RE = re.compile(
18
+ r"\b(?:red|known[- ]?failing|expected\s+to\s+fail|fail(?:s|ing)?\s+before\s+fix|"
19
+ r"regression)\b.{0,120}\btests?\b"
20
+ r"|\btests?\b.{0,120}\b(?:red|known[- ]?failing|expected\s+to\s+fail|"
21
+ r"fail(?:s|ing)?\s+before\s+fix|regression)\b",
22
+ re.IGNORECASE | re.DOTALL,
23
+ )
24
+ _SUPPORT_SCOPE_DIRS = frozenset(
25
+ {
26
+ ".github",
27
+ ".alysis",
28
+ "__tests__",
29
+ "doc",
30
+ "docs",
31
+ "example",
32
+ "examples",
33
+ "fixture",
34
+ "fixtures",
35
+ "note",
36
+ "notes",
37
+ "spec",
38
+ "specs",
39
+ "test",
40
+ "tests",
41
+ }
42
+ )
43
+ # ``completed_unverified`` belongs here: the task's work landed and was kept, only
44
+ # the authoritative check for it was missing. Treating it as unfinished would block
45
+ # every dependent task for a reason that has nothing to do with the work itself.
46
+ SUCCESSFUL_TERMINAL_STATUSES = frozenset(
47
+ {"done", "already_satisfied", TASK_STATUS_COMPLETED_UNVERIFIED}
48
+ )
49
+
50
+
51
+ def canonical_task_status(status: str) -> str:
52
+ value = (status or "").strip().lower()
53
+ if value == "todo":
54
+ return "planned"
55
+ return value or "planned"
56
+
57
+
58
+ @dataclass(frozen=True)
59
+ class TaskCandidate:
60
+ task_id: str
61
+ title: str
62
+ status: str
63
+ dependencies: tuple[str, ...]
64
+ estimated_files: frozenset[str]
65
+ write_scope: frozenset[str]
66
+ claimed_patterns: tuple[str, ...]
67
+ claimed_files: frozenset[str]
68
+ ambiguous_patterns: tuple[str, ...]
69
+ parallel_group: str
70
+ attempts: int
71
+ task: dict[str, Any]
72
+
73
+ @property
74
+ def has_estimated_files(self) -> bool:
75
+ return bool(self.estimated_files)
76
+
77
+ @property
78
+ def has_claimed_scope(self) -> bool:
79
+ return bool(self.claimed_patterns)
80
+
81
+ @property
82
+ def has_precise_claimed_scope(self) -> bool:
83
+ return bool(self.claimed_files) and not self.ambiguous_patterns
84
+
85
+ def claimed_scope_reason(self) -> str:
86
+ if not self.claimed_patterns:
87
+ return "missing claimed write scope metadata (estimated_files/write_scope)"
88
+ if self.ambiguous_patterns:
89
+ return f"ambiguous claimed scope: {_preview_items(self.ambiguous_patterns)}"
90
+ return f"precise claimed scope: {_preview_items(sorted(self.claimed_files))}"
91
+
92
+
93
+ @dataclass
94
+ class Batch:
95
+ index: int
96
+ task_ids: list[str] = field(default_factory=list)
97
+ reasons: list[str] = field(default_factory=list)
98
+
99
+
100
+ @dataclass
101
+ class Schedule:
102
+ base_branch: str
103
+ runnable: list[TaskCandidate]
104
+ ready_for_merge: list[TaskCandidate]
105
+ skipped: dict[str, str]
106
+ batches: list[Batch]
107
+
108
+
109
+ def _normalize_estimated_files(task: dict[str, Any]) -> frozenset[str]:
110
+ return frozenset(normalize_repo_path_list(task.get("estimated_files")))
111
+
112
+
113
+ def _normalize_write_scope(task: dict[str, Any]) -> frozenset[str]:
114
+ return frozenset(normalize_repo_path_list(task.get("write_scope")))
115
+
116
+
117
+ def _normalize_claimed_patterns(
118
+ task: dict[str, Any],
119
+ ) -> tuple[tuple[str, ...], frozenset[str], tuple[str, ...]]:
120
+ claimed = tuple(normalize_claimed_scope_patterns(task))
121
+ precise = frozenset(item for item in claimed if is_explicit_repo_path_pattern(item))
122
+ ambiguous = tuple(item for item in claimed if item not in precise)
123
+ return claimed, precise, ambiguous
124
+
125
+
126
+ def _preview_items(items: list[str] | tuple[str, ...], *, limit: int = 3) -> str:
127
+ values = list(items)
128
+ if not values:
129
+ return "(none)"
130
+ if len(values) <= limit:
131
+ return ", ".join(values)
132
+ hidden = len(values) - limit
133
+ return ", ".join(values[:limit]) + f", +{hidden} more"
134
+
135
+
136
+ def _task_text(task: dict[str, Any]) -> str:
137
+ acceptance = task.get("acceptance_criteria") or []
138
+ if not isinstance(acceptance, list):
139
+ acceptance = []
140
+ return " ".join(
141
+ part
142
+ for part in [
143
+ str(task.get("title") or ""),
144
+ str(task.get("description") or ""),
145
+ *(str(item or "") for item in acceptance),
146
+ ]
147
+ if part
148
+ )
149
+
150
+
151
+ def _is_test_or_support_scope_path(path: str) -> bool:
152
+ normalized = str(path or "").strip().replace("\\", "/").strip("/")
153
+ if not normalized:
154
+ return False
155
+ if normalized.endswith("/**"):
156
+ normalized = normalized[:-3].strip("/")
157
+ return is_test_path(normalized)
158
+
159
+
160
+ def _is_primary_implementation_scope_path(path: str) -> bool:
161
+ normalized = str(path or "").strip().replace("\\", "/").strip("/")
162
+ if not normalized:
163
+ return False
164
+ if _is_test_or_support_scope_path(normalized):
165
+ return False
166
+ if normalized.endswith("/**"):
167
+ parts = [part.casefold() for part in normalized[:-3].strip("/").split("/") if part]
168
+ return bool(parts) and not any(
169
+ part.startswith(".") or part in _SUPPORT_SCOPE_DIRS for part in parts
170
+ )
171
+ name = PurePosixPath(normalized).name.casefold()
172
+ if name in {"readme", "readme.md"} or name.endswith((".md", ".rst", ".txt")):
173
+ return False
174
+ if name in {
175
+ "cargo.toml",
176
+ "go.mod",
177
+ "package.json",
178
+ "pyproject.toml",
179
+ "requirements.txt",
180
+ "setup.py",
181
+ }:
182
+ return True
183
+ return is_code_implementation_path(normalized)
184
+
185
+
186
+ def _task_has_primary_implementation_scope(task: dict[str, Any]) -> bool:
187
+ paths = [
188
+ *normalize_repo_path_list(task.get("estimated_files")),
189
+ *normalize_repo_path_list(task.get("write_scope")),
190
+ ]
191
+ return any(_is_primary_implementation_scope_path(path) for path in paths)
192
+
193
+
194
+ def is_expected_red_regression_precursor(task: dict[str, Any]) -> bool:
195
+ text = _task_text(task)
196
+ if not _EXPECTED_RED_REGRESSION_TEXT_RE.search(text):
197
+ return False
198
+ paths = [
199
+ *normalize_repo_path_list(task.get("estimated_files")),
200
+ *normalize_repo_path_list(task.get("write_scope")),
201
+ ]
202
+ return bool(paths) and all(_is_test_or_support_scope_path(path) for path in paths)
203
+
204
+
205
+ def _effective_dependencies(
206
+ *,
207
+ task: dict[str, Any],
208
+ tasks: list[dict[str, Any]],
209
+ ) -> tuple[str, ...]:
210
+ deps_raw = task.get("dependencies") or []
211
+ deps = [str(x).strip() for x in deps_raw if str(x).strip()]
212
+ if not deps:
213
+ inferred = infer_ordered_predecessor_dependency(tasks=tasks, task=task)
214
+ if inferred is not None and inferred.depends_on not in deps:
215
+ deps.append(inferred.depends_on)
216
+ return tuple(deps)
217
+
218
+
219
+ def _to_candidate(task: dict[str, Any], *, tasks: list[dict[str, Any]]) -> TaskCandidate:
220
+ task_id = str(task.get("id") or "").strip()
221
+ title = str(task.get("title") or "").strip()
222
+ status = canonical_task_status(str(task.get("status") or ""))
223
+ deps = _effective_dependencies(task=task, tasks=tasks)
224
+ parallel_group = str(task.get("parallel_group") or "").strip()
225
+ attempts_raw = task.get("attempts")
226
+ try:
227
+ attempts = int(attempts_raw) if attempts_raw is not None else 0
228
+ except (TypeError, ValueError):
229
+ attempts = 0
230
+ if attempts < 0:
231
+ attempts = 0
232
+ estimated_files = _normalize_estimated_files(task)
233
+ write_scope = _normalize_write_scope(task)
234
+ claimed_patterns, claimed_files, ambiguous_patterns = _normalize_claimed_patterns(task)
235
+ return TaskCandidate(
236
+ task_id=task_id,
237
+ title=title,
238
+ status=status,
239
+ dependencies=deps,
240
+ estimated_files=estimated_files,
241
+ write_scope=write_scope,
242
+ claimed_patterns=claimed_patterns,
243
+ claimed_files=claimed_files,
244
+ ambiguous_patterns=ambiguous_patterns,
245
+ parallel_group=parallel_group,
246
+ attempts=attempts,
247
+ task=task,
248
+ )
249
+
250
+
251
+ def _runnable_status(
252
+ status: str,
253
+ *,
254
+ retry_failed: bool,
255
+ retry_changes_requested: bool,
256
+ ) -> bool:
257
+ if status in {"planned", "interrupted", "cancelled"}:
258
+ return True
259
+ if status in {"failed", "verify_failed", "candidate_rejected"} and retry_failed:
260
+ return True
261
+ if status == "changes_requested" and retry_changes_requested:
262
+ return True
263
+ return False
264
+
265
+
266
+ def _deps_done(
267
+ candidate: TaskCandidate,
268
+ *,
269
+ task_by_id: dict[str, TaskCandidate],
270
+ ) -> tuple[bool, str]:
271
+ for dep_id in candidate.dependencies:
272
+ dep = task_by_id.get(dep_id)
273
+ if dep is None:
274
+ return False, f"dependency missing: {dep_id}"
275
+ if (
276
+ dep.status == "ready_for_merge"
277
+ and is_expected_red_regression_precursor(dep.task)
278
+ and _task_has_primary_implementation_scope(candidate.task)
279
+ ):
280
+ continue
281
+ if dep.status not in SUCCESSFUL_TERMINAL_STATUSES:
282
+ return False, f"dependency not done: {dep_id} ({dep.status})"
283
+ return True, ""
284
+
285
+
286
+ def select_task_candidates(
287
+ *,
288
+ tasks: list[dict[str, Any]],
289
+ retry_failed: bool,
290
+ retry_changes_requested: bool,
291
+ max_attempts: int | None = None,
292
+ only_ids: set[str] | None = None,
293
+ ) -> tuple[list[TaskCandidate], list[TaskCandidate], dict[str, str]]:
294
+ candidates = [_to_candidate(task, tasks=tasks) for task in tasks]
295
+ task_by_id = {c.task_id: c for c in candidates}
296
+
297
+ runnable: list[TaskCandidate] = []
298
+ ready_for_merge: list[TaskCandidate] = []
299
+ skipped: dict[str, str] = {}
300
+
301
+ for candidate in candidates:
302
+ if not candidate.task_id:
303
+ continue
304
+
305
+ if only_ids is not None and candidate.task_id not in only_ids:
306
+ skipped[candidate.task_id] = "filtered by --only"
307
+ continue
308
+
309
+ if candidate.status in SUCCESSFUL_TERMINAL_STATUSES:
310
+ skipped[candidate.task_id] = "already done"
311
+ continue
312
+
313
+ if candidate.status == "ready_for_merge":
314
+ ready_for_merge.append(candidate)
315
+ continue
316
+
317
+ if not _runnable_status(
318
+ candidate.status,
319
+ retry_failed=retry_failed,
320
+ retry_changes_requested=retry_changes_requested,
321
+ ):
322
+ skipped[candidate.task_id] = f"status not runnable: {candidate.status}"
323
+ continue
324
+
325
+ if max_attempts is not None and candidate.attempts >= max_attempts:
326
+ skipped[candidate.task_id] = (
327
+ f"attempt limit reached: {candidate.attempts} >= {max_attempts}"
328
+ )
329
+ continue
330
+
331
+ ok, reason = _deps_done(candidate, task_by_id=task_by_id)
332
+ if not ok:
333
+ skipped[candidate.task_id] = reason
334
+ continue
335
+
336
+ runnable.append(candidate)
337
+
338
+ return runnable, ready_for_merge, skipped
339
+
340
+
341
+ def _pair_safe(a: TaskCandidate, b: TaskCandidate) -> tuple[bool, str]:
342
+ if not a.has_precise_claimed_scope:
343
+ return False, a.claimed_scope_reason()
344
+ if not b.has_precise_claimed_scope:
345
+ return False, b.claimed_scope_reason()
346
+ overlap = a.claimed_files & b.claimed_files
347
+ if overlap:
348
+ return False, f"claimed scope overlap: {_preview_items(sorted(overlap))}"
349
+ if a.parallel_group and b.parallel_group and a.parallel_group == b.parallel_group:
350
+ return False, f"same parallel_group: {a.parallel_group}"
351
+ return True, "claimed scopes disjoint"
352
+
353
+
354
+ def build_batches(
355
+ *,
356
+ runnable: list[TaskCandidate],
357
+ parallel: int,
358
+ ) -> list[Batch]:
359
+ if parallel <= 0:
360
+ parallel = 1
361
+ ordered = sorted(
362
+ runnable,
363
+ key=lambda c: (
364
+ not c.has_precise_claimed_scope,
365
+ c.task_id,
366
+ ),
367
+ )
368
+ runnable_by_id = {candidate.task_id: candidate for candidate in runnable}
369
+
370
+ batches: list[Batch] = []
371
+ for candidate in ordered:
372
+ placed = False
373
+ blockers: list[str] = []
374
+ if candidate.has_precise_claimed_scope:
375
+ for batch in batches:
376
+ if len(batch.task_ids) >= parallel:
377
+ continue
378
+ all_safe = True
379
+ reasons: list[str] = []
380
+ for existing_id in batch.task_ids:
381
+ existing = runnable_by_id[existing_id]
382
+ safe, reason = _pair_safe(candidate, existing)
383
+ if not safe:
384
+ all_safe = False
385
+ blockers.append(f"blocked by {existing.task_id}: {reason}")
386
+ break
387
+ reasons.append(f"{existing.task_id}: {reason}")
388
+ if all_safe:
389
+ batch.task_ids.append(candidate.task_id)
390
+ batch.reasons.append(f"{candidate.task_id} joined batch: {', '.join(reasons)}")
391
+ placed = True
392
+ break
393
+
394
+ if not placed:
395
+ idx = len(batches) + 1
396
+ batch = Batch(index=idx, task_ids=[candidate.task_id])
397
+ if not candidate.has_precise_claimed_scope:
398
+ batch.reasons.append(
399
+ f"{candidate.task_id} runs alone: {candidate.claimed_scope_reason()}"
400
+ )
401
+ elif blockers:
402
+ batch.reasons.append(
403
+ f"{candidate.task_id} starts new batch: {_preview_items(blockers, limit=2)}"
404
+ )
405
+ else:
406
+ batch.reasons.append(
407
+ f"{candidate.task_id} starts new batch: {candidate.claimed_scope_reason()}"
408
+ )
409
+ batches.append(batch)
410
+
411
+ return batches
412
+
413
+
414
+ def compute_schedule(
415
+ *,
416
+ base_branch: str,
417
+ tasks: list[dict[str, Any]],
418
+ parallel: int,
419
+ max_tasks: int | None,
420
+ retry_failed: bool,
421
+ retry_changes_requested: bool = False,
422
+ max_attempts: int | None = None,
423
+ only_ids: set[str] | None = None,
424
+ ) -> Schedule:
425
+ runnable, ready_for_merge, skipped = select_task_candidates(
426
+ tasks=tasks,
427
+ retry_failed=retry_failed,
428
+ retry_changes_requested=retry_changes_requested,
429
+ max_attempts=max_attempts,
430
+ only_ids=only_ids,
431
+ )
432
+ if max_tasks is not None and max_tasks > 0:
433
+ runnable = runnable[:max_tasks]
434
+ batches = build_batches(runnable=runnable, parallel=parallel)
435
+ return Schedule(
436
+ base_branch=base_branch,
437
+ runnable=runnable,
438
+ ready_for_merge=ready_for_merge,
439
+ skipped=skipped,
440
+ batches=batches,
441
+ )