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,362 @@
1
+ """Post-execution completion report for Forge swarm runs.
2
+
3
+ ``/execute plan`` historically ended with a one-line headline plus a pointer at
4
+ ``swarm_summary.md`` — the run's actual results (what each worker built, where
5
+ the files landed, whether the output is trustworthy, and how to try it) stayed
6
+ buried in run artifacts nothing surfaced. This module turns those artifacts
7
+ into one user-facing markdown report, deterministically on the host side: no
8
+ model call, so it works identically for every provider and costs nothing.
9
+
10
+ Inputs are read best-effort from the run directory (``plan.json`` statuses,
11
+ ``execution/worker_results/*.json``, the per-task
12
+ ``knowledge_capture/<task>/<ts>/assistant_message.md`` worker narratives) so a
13
+ partially-written run still produces an honest, if sparser, report.
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import json
19
+ from pathlib import Path
20
+ from typing import Any
21
+
22
+ from .verification_repair import TASK_STATUS_COMPLETED_UNVERIFIED
23
+
24
+ _DONE_STATES = frozenset({"done"})
25
+ _NOOP_STATES = frozenset({"already_satisfied"})
26
+ # Its own bucket on purpose: counted as finished (the work landed and was kept), but
27
+ # rendered with the caveat, because nothing authoritative checked it.
28
+ _UNVERIFIED_STATES = frozenset({TASK_STATUS_COMPLETED_UNVERIFIED})
29
+ _FAILURE_STATES = frozenset(
30
+ {
31
+ "failed",
32
+ "verify_failed",
33
+ "candidate_rejected",
34
+ "changes_requested",
35
+ "merge_conflict",
36
+ "blocked_integration",
37
+ "blocked",
38
+ "interrupted",
39
+ "cancelled",
40
+ }
41
+ )
42
+ _OBSOLETE_STATES = frozenset({"superseded", "invalidated"})
43
+ _MAX_FILES_SHOWN = 12
44
+ _MAX_NOTE_CHARS = 140
45
+ _MAX_REASON_CHARS = 160
46
+ _MAX_HINTS = 3
47
+ _RUN_SCRIPT_PRIORITY = ("dev", "start", "serve", "preview")
48
+
49
+
50
+ def _canonical_status(value: object) -> str:
51
+ status = str(value or "").strip().lower()
52
+ try:
53
+ from .swarm_scheduler import canonical_task_status
54
+
55
+ return canonical_task_status(status)
56
+ except Exception: # noqa: BLE001
57
+ return status or "planned"
58
+
59
+
60
+ def _current_branch(root: Path) -> str:
61
+ try:
62
+ from .git_ops import current_branch
63
+
64
+ return str(current_branch(root)).strip()
65
+ except Exception: # noqa: BLE001
66
+ return ""
67
+
68
+
69
+ def _load_worker_results(paths: Any) -> dict[str, dict[str, Any]]:
70
+ """Index ``execution/worker_results/*.json`` payloads by task id."""
71
+ execution_dir = getattr(paths, "execution_dir", None)
72
+ if execution_dir is None:
73
+ return {}
74
+ results_dir = Path(execution_dir) / "worker_results"
75
+ indexed: dict[str, dict[str, Any]] = {}
76
+ try:
77
+ entries = sorted(results_dir.glob("*.json"))
78
+ except Exception: # noqa: BLE001
79
+ return {}
80
+ for entry in entries:
81
+ try:
82
+ payload = json.loads(entry.read_text(encoding="utf-8"))
83
+ except Exception: # noqa: BLE001
84
+ continue
85
+ if not isinstance(payload, dict):
86
+ continue
87
+ task_id = str(payload.get("task_id") or "").strip()
88
+ if task_id:
89
+ indexed[task_id] = payload
90
+ return indexed
91
+
92
+
93
+ def _worker_note(worker_payload: dict[str, Any]) -> str:
94
+ """First meaningful line of the worker's own final answer, if captured."""
95
+ capture_dir = str(worker_payload.get("knowledge_capture_artifact_dir") or "").strip()
96
+ if not capture_dir:
97
+ return ""
98
+ message_path = Path(capture_dir) / "assistant_message.md"
99
+ try:
100
+ text = message_path.read_text(encoding="utf-8")
101
+ except Exception: # noqa: BLE001
102
+ return ""
103
+ for raw_line in text.splitlines():
104
+ line = raw_line.strip()
105
+ if not line or line == "(none captured)":
106
+ continue
107
+ if line.startswith(("#", "```", "---", "KNOWLEDGE")):
108
+ continue
109
+ line = line.lstrip("-*> ").strip()
110
+ if not line:
111
+ continue
112
+ if len(line) > _MAX_NOTE_CHARS:
113
+ line = line[: _MAX_NOTE_CHARS - 1].rstrip() + "…"
114
+ return line
115
+ return ""
116
+
117
+
118
+ def _failure_reason(task: dict[str, Any], worker_payload: dict[str, Any]) -> str:
119
+ for candidate in (
120
+ task.get("last_error"),
121
+ worker_payload.get("failure_reason"),
122
+ worker_payload.get("error"),
123
+ worker_payload.get("summary"),
124
+ ):
125
+ reason = " ".join(str(candidate or "").split())
126
+ if reason:
127
+ if len(reason) > _MAX_REASON_CHARS:
128
+ reason = reason[: _MAX_REASON_CHARS - 1].rstrip() + "…"
129
+ return reason
130
+ return "see the task report for details"
131
+
132
+
133
+ def _shallowest_existing(root: Path, rel_paths: list[str]) -> Path | None:
134
+ for rel in sorted(rel_paths, key=lambda item: (item.count("/"), item)):
135
+ candidate = root / Path(rel)
136
+ try:
137
+ if candidate.is_file():
138
+ return candidate
139
+ except OSError:
140
+ continue
141
+ return None
142
+
143
+
144
+ def _package_json_hint(pkg_path: Path) -> str:
145
+ try:
146
+ payload = json.loads(pkg_path.read_text(encoding="utf-8"))
147
+ except Exception: # noqa: BLE001
148
+ return ""
149
+ scripts = payload.get("scripts")
150
+ if not isinstance(scripts, dict):
151
+ return ""
152
+ script_name = next(
153
+ (name for name in _RUN_SCRIPT_PRIORITY if str(scripts.get(name) or "").strip()),
154
+ "",
155
+ )
156
+ if not script_name:
157
+ return ""
158
+ run_cmd = "npm start" if script_name == "start" else f"npm run {script_name}"
159
+ pkg_dir = pkg_path.parent
160
+ needs_install = not (pkg_dir / "node_modules").is_dir()
161
+ command = f"npm install && {run_cmd}" if needs_install else run_cmd
162
+ return f'Node app: `cd "{pkg_dir}"` then `{command}` (package.json `{script_name}` script)'
163
+
164
+
165
+ def detect_run_hints(root: Path, changed_files: list[str] | None = None) -> list[str]:
166
+ """Grounded "try it" suggestions: only commands whose target files exist.
167
+
168
+ Prefers files this run actually changed over pre-existing ones, and always
169
+ names absolute directories — the workspace root the swarm merged into is
170
+ not necessarily the directory the user's shell sits in.
171
+ """
172
+ root = Path(root)
173
+ normalized = [str(item).replace("\\", "/").strip("/") for item in (changed_files or [])]
174
+ hints: list[str] = []
175
+
176
+ index_candidates = [rel for rel in normalized if rel.rsplit("/", 1)[-1].lower() == "index.html"]
177
+ index_path = _shallowest_existing(root, index_candidates)
178
+ if index_path is None:
179
+ try:
180
+ if (root / "index.html").is_file():
181
+ index_path = root / "index.html"
182
+ except OSError:
183
+ index_path = None
184
+ if index_path is not None:
185
+ serve_dir = index_path.parent
186
+ hints.append(
187
+ f"Static site: open `{index_path}` in a browser, or serve it — "
188
+ f'`cd "{serve_dir}"` then `python -m http.server 8000` and visit '
189
+ "http://localhost:8000"
190
+ )
191
+
192
+ pkg_candidates = [rel for rel in normalized if rel.rsplit("/", 1)[-1] == "package.json"]
193
+ pkg_path = _shallowest_existing(root, pkg_candidates)
194
+ if pkg_path is None:
195
+ try:
196
+ if (root / "package.json").is_file():
197
+ pkg_path = root / "package.json"
198
+ except OSError:
199
+ pkg_path = None
200
+ if pkg_path is not None:
201
+ pkg_hint = _package_json_hint(pkg_path)
202
+ if pkg_hint:
203
+ hints.append(pkg_hint)
204
+
205
+ return hints[:_MAX_HINTS]
206
+
207
+
208
+ def _task_rows(
209
+ tasks: list[dict[str, Any]],
210
+ worker_results: dict[str, dict[str, Any]],
211
+ ) -> tuple[list[str], list[str], int, int, int, int]:
212
+ """Render per-task lines; return (rows, merged files, done, noop, failed, remaining)."""
213
+ rows: list[str] = []
214
+ merged_files: list[str] = []
215
+ done = noop = failed = remaining = 0
216
+ for task in tasks:
217
+ if not isinstance(task, dict):
218
+ remaining += 1
219
+ continue
220
+ task_id = str(task.get("id") or "").strip() or "?"
221
+ title = " ".join(str(task.get("title") or "").split()) or "(untitled task)"
222
+ status = _canonical_status(task.get("status"))
223
+ worker_payload = worker_results.get(task_id) or {}
224
+ changed = [
225
+ str(item).strip()
226
+ for item in (worker_payload.get("changed_files") or [])
227
+ if str(item).strip()
228
+ ]
229
+ if status in _DONE_STATES:
230
+ done += 1
231
+ merged_files.extend(changed)
232
+ detail = f"{len(changed)} file{'s' if len(changed) != 1 else ''} changed"
233
+ note = _worker_note(worker_payload)
234
+ row = f"- ✓ **{task_id} · {title}** — {detail}"
235
+ if note:
236
+ row += f" — {note}"
237
+ if bool(worker_payload.get("verify_failed")):
238
+ row += " ⚠ its checks failed but were tolerated (verify mode: warn)"
239
+ rows.append(row)
240
+ elif status in _UNVERIFIED_STATES:
241
+ done += 1
242
+ merged_files.extend(changed)
243
+ detail = f"{len(changed)} file{'s' if len(changed) != 1 else ''} changed"
244
+ note = _worker_note(worker_payload)
245
+ row = (
246
+ f"- ✓ **{task_id} · {title}** — {detail} — ⚠ kept but UNVERIFIED: "
247
+ "no authoritative verification command exists for this workspace"
248
+ )
249
+ if note:
250
+ row += f" — {note}"
251
+ rows.append(row)
252
+ elif status in _NOOP_STATES:
253
+ noop += 1
254
+ rows.append(
255
+ f"- ○ **{task_id} · {title}** — reported no changes needed "
256
+ "(already satisfied); nothing was merged for it"
257
+ )
258
+ elif status in _FAILURE_STATES:
259
+ failed += 1
260
+ rows.append(
261
+ f"- ✗ **{task_id} · {title}** — {status}: {_failure_reason(task, worker_payload)}"
262
+ )
263
+ elif status in _OBSOLETE_STATES:
264
+ rows.append(f"- · **{task_id} · {title}** — {status}")
265
+ else:
266
+ remaining += 1
267
+ rows.append(f"- … **{task_id} · {title}** — {status or 'planned'}")
268
+ return rows, merged_files, done, noop, failed, remaining
269
+
270
+
271
+ def build_forge_completion_report(
272
+ *,
273
+ paths: Any,
274
+ plan: dict[str, Any],
275
+ run_status: str = "",
276
+ run_clean: bool | None = None,
277
+ exit_code: int = 0,
278
+ ) -> str:
279
+ """Render the post-run answer shown to the user after ``/execute plan``."""
280
+ tasks = list(plan.get("tasks") or [])
281
+ worker_results = _load_worker_results(paths)
282
+ rows, merged_files, done, noop, failed, remaining = _task_rows(tasks, worker_results)
283
+ total = len(tasks)
284
+ root = Path(getattr(paths, "root", "."))
285
+
286
+ finished = done + noop
287
+ if total <= 0:
288
+ headline = "### Forge execution finished — no tasks were in the plan"
289
+ elif failed == 0 and remaining == 0 and exit_code == 0:
290
+ headline = f"### Forge execution complete — {finished}/{total} tasks finished"
291
+ else:
292
+ parts = [f"{finished} finished"]
293
+ if failed:
294
+ parts.append(f"{failed} failed")
295
+ if remaining:
296
+ parts.append(f"{remaining} not finished")
297
+ headline = "### Forge execution finished with issues — " + " · ".join(parts)
298
+
299
+ lines: list[str] = [headline, ""]
300
+
301
+ unique_files = sorted(dict.fromkeys(merged_files))
302
+ branch = _current_branch(root)
303
+ if unique_files:
304
+ where = f"branch `{branch}` in `{root}`" if branch else f"`{root}`"
305
+ lines.append(f"**Where the work landed:** merged into {where}.")
306
+ elif total > 0:
307
+ lines.append(
308
+ "⚠ **No files were changed by this run.** If you expected new files "
309
+ "(for example a website), nothing was actually written to "
310
+ f"`{root}` — check the per-task lines and reports below."
311
+ )
312
+ lines.append("")
313
+
314
+ if rows:
315
+ lines.append("**Tasks:**")
316
+ lines.extend(rows)
317
+ lines.append("")
318
+
319
+ if unique_files:
320
+ shown = unique_files[:_MAX_FILES_SHOWN]
321
+ extra = len(unique_files) - len(shown)
322
+ listing = ", ".join(f"`{item}`" for item in shown)
323
+ if extra > 0:
324
+ listing += f" (+{extra} more)"
325
+ lines.append(f"**Files changed ({len(unique_files)}):** {listing}")
326
+ lines.append("")
327
+
328
+ warn_verified = sum(
329
+ 1
330
+ for payload in worker_results.values()
331
+ if bool(payload.get("verify_failed")) and bool(payload.get("success"))
332
+ )
333
+ if run_clean is False and run_status:
334
+ lines.append(
335
+ f"⚠ **Verification:** run status is `{run_status}` — some checks failed, "
336
+ "were tolerated, or never ran; treat completed tasks as unverified."
337
+ )
338
+ lines.append("")
339
+ elif warn_verified:
340
+ lines.append(
341
+ f"⚠ **Verification:** {warn_verified} task(s) merged with failing checks "
342
+ "(verify mode: warn)."
343
+ )
344
+ lines.append("")
345
+
346
+ if unique_files:
347
+ hints = detect_run_hints(root, unique_files)
348
+ if hints:
349
+ lines.append("**Try it:**")
350
+ lines.extend(f"- {hint}" for hint in hints)
351
+ lines.append("")
352
+
353
+ execution_dir = getattr(paths, "execution_dir", None)
354
+ if execution_dir is not None:
355
+ summary_path = Path(execution_dir) / "swarm_summary.md"
356
+ reports_dir = getattr(paths, "execution_reports_dir", Path(execution_dir) / "reports")
357
+ lines.append(f"**Details:** `{summary_path}` · per-task reports in `{reports_dir}`")
358
+
359
+ return "\n".join(lines).strip()
360
+
361
+
362
+ __all__ = ["build_forge_completion_report", "detect_run_hints"]