iCode-TUI 0.29.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1138) hide show
  1. chrys/__init__.py +16 -0
  2. chrys/app/__init__.py +3 -0
  3. chrys/app/acp/__init__.py +3 -0
  4. chrys/app/acp/bridge.py +664 -0
  5. chrys/app/acp/content.py +132 -0
  6. chrys/app/acp/doc/frontend-api.md +425 -0
  7. chrys/app/acp/history.py +585 -0
  8. chrys/app/acp/server.py +1796 -0
  9. chrys/app/acp/session_manager.py +1815 -0
  10. chrys/app/acp/tool_status.py +45 -0
  11. chrys/app/cli/__init__.py +3 -0
  12. chrys/app/cli/acp.py +178 -0
  13. chrys/app/cli/app.py +165 -0
  14. chrys/app/cli/approvals.py +101 -0
  15. chrys/app/cli/headless.py +223 -0
  16. chrys/app/cli/launch_cwd.py +42 -0
  17. chrys/app/cli/profiles.py +298 -0
  18. chrys/app/cli/progress.py +857 -0
  19. chrys/app/cli/run.py +425 -0
  20. chrys/app/cli/serve.py +1593 -0
  21. chrys/app/cli/tool_summary.py +225 -0
  22. chrys/app/cli/trajectory.py +201 -0
  23. chrys/app/cli/workflow.py +601 -0
  24. chrys/app/features/__init__.py +3 -0
  25. chrys/app/features/buddy/__init__.py +3 -0
  26. chrys/app/features/buddy/actions.py +68 -0
  27. chrys/app/features/buddy/animation.py +76 -0
  28. chrys/app/features/buddy/commands.py +196 -0
  29. chrys/app/features/buddy/docs/design.md +187 -0
  30. chrys/app/features/buddy/hatchery.py +80 -0
  31. chrys/app/features/buddy/lifecycle.py +41 -0
  32. chrys/app/features/buddy/model.py +265 -0
  33. chrys/app/features/buddy/pixel_renderer.py +113 -0
  34. chrys/app/features/buddy/pixel_sprites.py +279 -0
  35. chrys/app/features/buddy/portrait.py +76 -0
  36. chrys/app/features/buddy/progression.py +58 -0
  37. chrys/app/features/buddy/replies.py +191 -0
  38. chrys/app/features/buddy/sprites/alien.toml +70 -0
  39. chrys/app/features/buddy/sprites/axolotl.toml +71 -0
  40. chrys/app/features/buddy/sprites/bat.toml +69 -0
  41. chrys/app/features/buddy/sprites/bee.toml +69 -0
  42. chrys/app/features/buddy/sprites/cactus.toml +73 -0
  43. chrys/app/features/buddy/sprites/capybara.toml +70 -0
  44. chrys/app/features/buddy/sprites/cat.toml +69 -0
  45. chrys/app/features/buddy/sprites/crab.toml +70 -0
  46. chrys/app/features/buddy/sprites/dragon.toml +71 -0
  47. chrys/app/features/buddy/sprites/duck.toml +69 -0
  48. chrys/app/features/buddy/sprites/fox.toml +69 -0
  49. chrys/app/features/buddy/sprites/frog.toml +70 -0
  50. chrys/app/features/buddy/sprites/ghost.toml +70 -0
  51. chrys/app/features/buddy/sprites/goose.toml +69 -0
  52. chrys/app/features/buddy/sprites/jelly.toml +70 -0
  53. chrys/app/features/buddy/sprites/llama.toml +70 -0
  54. chrys/app/features/buddy/sprites/mushroom.toml +69 -0
  55. chrys/app/features/buddy/sprites/octopus.toml +70 -0
  56. chrys/app/features/buddy/sprites/owl.toml +70 -0
  57. chrys/app/features/buddy/sprites/panda.toml +71 -0
  58. chrys/app/features/buddy/sprites/penguin.toml +69 -0
  59. chrys/app/features/buddy/sprites/rabbit.toml +70 -0
  60. chrys/app/features/buddy/sprites/robot.toml +72 -0
  61. chrys/app/features/buddy/sprites/shark.toml +70 -0
  62. chrys/app/features/buddy/sprites/snail.toml +69 -0
  63. chrys/app/features/buddy/sprites/snake.toml +69 -0
  64. chrys/app/features/buddy/sprites/tabby.toml +69 -0
  65. chrys/app/features/buddy/sprites/turtle.toml +69 -0
  66. chrys/app/features/buddy/store.py +143 -0
  67. chrys/app/features/session_title/__init__.py +7 -0
  68. chrys/app/features/session_title/updater.py +582 -0
  69. chrys/app/installer.py +651 -0
  70. chrys/app/parsing.py +17 -0
  71. chrys/app/tui/__init__.py +3 -0
  72. chrys/app/tui/app.py +1287 -0
  73. chrys/app/tui/behaviors/__init__.py +3 -0
  74. chrys/app/tui/behaviors/click_outside_dismiss.py +90 -0
  75. chrys/app/tui/behaviors/insert_clipboard.py +73 -0
  76. chrys/app/tui/behaviors/right_click_copy.py +146 -0
  77. chrys/app/tui/binding_display.py +139 -0
  78. chrys/app/tui/buddy_messages.py +17 -0
  79. chrys/app/tui/buddy_reply.py +86 -0
  80. chrys/app/tui/chrys.tcss +549 -0
  81. chrys/app/tui/clipboard.py +109 -0
  82. chrys/app/tui/copy_messages.py +10 -0
  83. chrys/app/tui/i18n.py +230 -0
  84. chrys/app/tui/language.py +58 -0
  85. chrys/app/tui/notifications/__init__.py +12 -0
  86. chrys/app/tui/notifications/drivers.py +180 -0
  87. chrys/app/tui/notifications/service.py +126 -0
  88. chrys/app/tui/notifications/settings.py +141 -0
  89. chrys/app/tui/screens/__init__.py +3 -0
  90. chrys/app/tui/screens/agents/__init__.py +8 -0
  91. chrys/app/tui/screens/agents/agent_draft_store.py +937 -0
  92. chrys/app/tui/screens/agents/config.py +2142 -0
  93. chrys/app/tui/screens/agents/config.tcss +169 -0
  94. chrys/app/tui/screens/agents/panels/__init__.py +3 -0
  95. chrys/app/tui/screens/agents/panels/acp.py +1176 -0
  96. chrys/app/tui/screens/agents/panels/basic.py +497 -0
  97. chrys/app/tui/screens/agents/panels/compaction.py +231 -0
  98. chrys/app/tui/screens/agents/panels/config_card.py +218 -0
  99. chrys/app/tui/screens/agents/panels/instructions.py +91 -0
  100. chrys/app/tui/screens/agents/panels/markdown_report.py +76 -0
  101. chrys/app/tui/screens/agents/panels/mcp.py +2262 -0
  102. chrys/app/tui/screens/agents/panels/memory.py +684 -0
  103. chrys/app/tui/screens/agents/panels/path_entry.py +227 -0
  104. chrys/app/tui/screens/agents/panels/skills.py +806 -0
  105. chrys/app/tui/screens/agents/panels/subagents.py +652 -0
  106. chrys/app/tui/screens/agents/panels/tools.py +205 -0
  107. chrys/app/tui/screens/agents/picker.py +103 -0
  108. chrys/app/tui/screens/agents/skill_paths.py +38 -0
  109. chrys/app/tui/screens/agents/validation_messages.py +452 -0
  110. chrys/app/tui/screens/dialogs/__init__.py +35 -0
  111. chrys/app/tui/screens/dialogs/agent_load.py +834 -0
  112. chrys/app/tui/screens/dialogs/agent_load.tcss +78 -0
  113. chrys/app/tui/screens/dialogs/app_mode.py +55 -0
  114. chrys/app/tui/screens/dialogs/approval/__init__.py +15 -0
  115. chrys/app/tui/screens/dialogs/approval/approval.tcss +311 -0
  116. chrys/app/tui/screens/dialogs/approval/bodies/__init__.py +3 -0
  117. chrys/app/tui/screens/dialogs/approval/bodies/file_edit.py +210 -0
  118. chrys/app/tui/screens/dialogs/approval/bodies/sub_agent.py +82 -0
  119. chrys/app/tui/screens/dialogs/approval/body.py +110 -0
  120. chrys/app/tui/screens/dialogs/approval/dialog.py +565 -0
  121. chrys/app/tui/screens/dialogs/approval/mode.py +105 -0
  122. chrys/app/tui/screens/dialogs/ask_user.py +148 -0
  123. chrys/app/tui/screens/dialogs/ask_user.tcss +62 -0
  124. chrys/app/tui/screens/dialogs/base.py +84 -0
  125. chrys/app/tui/screens/dialogs/confirm.py +184 -0
  126. chrys/app/tui/screens/dialogs/confirm.tcss +46 -0
  127. chrys/app/tui/screens/dialogs/connection_test.py +183 -0
  128. chrys/app/tui/screens/dialogs/connection_test.tcss +92 -0
  129. chrys/app/tui/screens/dialogs/diagram.py +102 -0
  130. chrys/app/tui/screens/dialogs/diagram.tcss +24 -0
  131. chrys/app/tui/screens/dialogs/editor.py +378 -0
  132. chrys/app/tui/screens/dialogs/editor.tcss +132 -0
  133. chrys/app/tui/screens/dialogs/file_picker.py +582 -0
  134. chrys/app/tui/screens/dialogs/file_picker.tcss +78 -0
  135. chrys/app/tui/screens/dialogs/fork_session.py +219 -0
  136. chrys/app/tui/screens/dialogs/fork_session.tcss +75 -0
  137. chrys/app/tui/screens/dialogs/image_compression.py +102 -0
  138. chrys/app/tui/screens/dialogs/image_compression.tcss +28 -0
  139. chrys/app/tui/screens/dialogs/man_page.py +172 -0
  140. chrys/app/tui/screens/dialogs/man_page.tcss +39 -0
  141. chrys/app/tui/screens/dialogs/new_folder.py +277 -0
  142. chrys/app/tui/screens/dialogs/new_folder.tcss +51 -0
  143. chrys/app/tui/screens/dialogs/notice.tcss +14 -0
  144. chrys/app/tui/screens/dialogs/prompt_cache.py +144 -0
  145. chrys/app/tui/screens/dialogs/prompt_cache.tcss +38 -0
  146. chrys/app/tui/screens/dialogs/runtime_details.py +371 -0
  147. chrys/app/tui/screens/dialogs/runtime_details.tcss +49 -0
  148. chrys/app/tui/screens/dialogs/session_title.py +110 -0
  149. chrys/app/tui/screens/dialogs/session_title.tcss +43 -0
  150. chrys/app/tui/screens/dialogs/tool_view.py +207 -0
  151. chrys/app/tui/screens/dialogs/tool_view.tcss +43 -0
  152. chrys/app/tui/screens/dialogs/vision_unsupported.py +122 -0
  153. chrys/app/tui/screens/dialogs/vision_unsupported.tcss +35 -0
  154. chrys/app/tui/screens/dialogs/workflow_confirm.py +91 -0
  155. chrys/app/tui/screens/dialogs/workflow_confirm.tcss +17 -0
  156. chrys/app/tui/screens/dialogs/workflow_input.py +258 -0
  157. chrys/app/tui/screens/dialogs/workflow_input.tcss +73 -0
  158. chrys/app/tui/screens/dialogs/workflow_node.py +462 -0
  159. chrys/app/tui/screens/dialogs/workflow_node.tcss +41 -0
  160. chrys/app/tui/screens/dialogs/workflow_picker.py +46 -0
  161. chrys/app/tui/screens/dialogs/workflow_result.py +136 -0
  162. chrys/app/tui/screens/dialogs/workflow_result.tcss +14 -0
  163. chrys/app/tui/screens/diff/__init__.py +22 -0
  164. chrys/app/tui/screens/diff/rollback_modal.py +1381 -0
  165. chrys/app/tui/screens/diff/rollback_modal.tcss +248 -0
  166. chrys/app/tui/screens/diff/screen.py +1532 -0
  167. chrys/app/tui/screens/diff/screen.tcss +104 -0
  168. chrys/app/tui/screens/guides/__init__.py +7 -0
  169. chrys/app/tui/screens/guides/_docs/en/guides/configuration/agents.md +178 -0
  170. chrys/app/tui/screens/guides/_docs/en/guides/configuration/approval.md +117 -0
  171. chrys/app/tui/screens/guides/_docs/en/guides/configuration/chat-options.md +276 -0
  172. chrys/app/tui/screens/guides/_docs/en/guides/configuration/compaction.md +42 -0
  173. chrys/app/tui/screens/guides/_docs/en/guides/configuration/memory.md +48 -0
  174. chrys/app/tui/screens/guides/_docs/en/guides/configuration/models.md +158 -0
  175. chrys/app/tui/screens/guides/_docs/en/guides/configuration/settings.md +210 -0
  176. chrys/app/tui/screens/guides/_docs/en/guides/configuration/themes.md +61 -0
  177. chrys/app/tui/screens/guides/_docs/en/guides/configuration/tools.md +60 -0
  178. chrys/app/tui/screens/guides/_docs/en/guides/configuration/web-tools.md +286 -0
  179. chrys/app/tui/screens/guides/_docs/en/guides/daily-use/sessions.md +77 -0
  180. chrys/app/tui/screens/guides/_docs/en/guides/daily-use/workspaces.md +51 -0
  181. chrys/app/tui/screens/guides/_docs/en/guides/extensions/external-acp-agents.md +126 -0
  182. chrys/app/tui/screens/guides/_docs/en/guides/extensions/hooks.md +167 -0
  183. chrys/app/tui/screens/guides/_docs/en/guides/extensions/mcp.md +95 -0
  184. chrys/app/tui/screens/guides/_docs/en/guides/extensions/skills.md +175 -0
  185. chrys/app/tui/screens/guides/_docs/en/guides/running/icode-acp.md +61 -0
  186. chrys/app/tui/screens/guides/_docs/en/guides/running/icode-run.md +190 -0
  187. chrys/app/tui/screens/guides/_docs/en/guides/running/icode-serve.md +239 -0
  188. chrys/app/tui/screens/guides/_docs/en/guides/running/workflows.md +664 -0
  189. chrys/app/tui/screens/guides/_docs/en/reference/agent-profile.md +548 -0
  190. chrys/app/tui/screens/guides/_docs/en/reference/hooks.md +602 -0
  191. chrys/app/tui/screens/guides/_docs/en/reference/opentelemetry.md +177 -0
  192. chrys/app/tui/screens/guides/_docs/en/reference/settings.md +245 -0
  193. chrys/app/tui/screens/guides/_docs/en/reference/themes.md +149 -0
  194. chrys/app/tui/screens/guides/_docs/en/reference/tool-kinds-and-names.md +58 -0
  195. chrys/app/tui/screens/guides/_docs/en/reference/tui-slash-commands.md +76 -0
  196. chrys/app/tui/screens/guides/_docs/en/reference/workflows.md +1006 -0
  197. chrys/app/tui/screens/guides/_docs/en/start/getting-started.md +170 -0
  198. chrys/app/tui/screens/guides/_docs/en/start/what-is-icode.md +34 -0
  199. chrys/app/tui/screens/guides/_docs/index.yaml +100 -0
  200. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/agents.md +178 -0
  201. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/approval.md +117 -0
  202. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/chat-options.md +276 -0
  203. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/compaction.md +42 -0
  204. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/memory.md +48 -0
  205. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/models.md +158 -0
  206. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/settings.md +210 -0
  207. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/themes.md +61 -0
  208. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/tools.md +60 -0
  209. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/web-tools.md +286 -0
  210. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/daily-use/sessions.md +77 -0
  211. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/daily-use/workspaces.md +51 -0
  212. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/extensions/external-acp-agents.md +126 -0
  213. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/extensions/hooks.md +167 -0
  214. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/extensions/mcp.md +95 -0
  215. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/extensions/skills.md +175 -0
  216. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/running/icode-acp.md +61 -0
  217. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/running/icode-run.md +190 -0
  218. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/running/icode-serve.md +239 -0
  219. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/running/workflows.md +664 -0
  220. chrys/app/tui/screens/guides/_docs/zh-Hans/reference/agent-profile.md +548 -0
  221. chrys/app/tui/screens/guides/_docs/zh-Hans/reference/hooks.md +602 -0
  222. chrys/app/tui/screens/guides/_docs/zh-Hans/reference/opentelemetry.md +177 -0
  223. chrys/app/tui/screens/guides/_docs/zh-Hans/reference/settings.md +245 -0
  224. chrys/app/tui/screens/guides/_docs/zh-Hans/reference/themes.md +149 -0
  225. chrys/app/tui/screens/guides/_docs/zh-Hans/reference/tool-kinds-and-names.md +58 -0
  226. chrys/app/tui/screens/guides/_docs/zh-Hans/reference/tui-slash-commands.md +76 -0
  227. chrys/app/tui/screens/guides/_docs/zh-Hans/reference/workflows.md +1005 -0
  228. chrys/app/tui/screens/guides/_docs/zh-Hans/start/getting-started.md +170 -0
  229. chrys/app/tui/screens/guides/_docs/zh-Hans/start/what-is-icode.md +34 -0
  230. chrys/app/tui/screens/guides/index.py +215 -0
  231. chrys/app/tui/screens/guides/reader.py +173 -0
  232. chrys/app/tui/screens/guides/screen.py +442 -0
  233. chrys/app/tui/screens/guides/screen.tcss +52 -0
  234. chrys/app/tui/screens/logs/__init__.py +7 -0
  235. chrys/app/tui/screens/logs/screen.py +504 -0
  236. chrys/app/tui/screens/logs/screen.tcss +45 -0
  237. chrys/app/tui/screens/main/__init__.py +7 -0
  238. chrys/app/tui/screens/main/action_availability.py +30 -0
  239. chrys/app/tui/screens/main/buddy_command.py +143 -0
  240. chrys/app/tui/screens/main/commands.py +1073 -0
  241. chrys/app/tui/screens/main/config_actions.py +464 -0
  242. chrys/app/tui/screens/main/copy_actions.py +117 -0
  243. chrys/app/tui/screens/main/dialog_controllers.py +826 -0
  244. chrys/app/tui/screens/main/dialog_gateway.py +166 -0
  245. chrys/app/tui/screens/main/diff_controller.py +478 -0
  246. chrys/app/tui/screens/main/engine_read_model.py +184 -0
  247. chrys/app/tui/screens/main/event_handlers.py +2014 -0
  248. chrys/app/tui/screens/main/input_flow.py +241 -0
  249. chrys/app/tui/screens/main/live_diff.py +244 -0
  250. chrys/app/tui/screens/main/main_screen_presenter.py +295 -0
  251. chrys/app/tui/screens/main/model_indicator.py +217 -0
  252. chrys/app/tui/screens/main/navigation.py +343 -0
  253. chrys/app/tui/screens/main/ports.py +571 -0
  254. chrys/app/tui/screens/main/recent_dirs.py +123 -0
  255. chrys/app/tui/screens/main/rollback_controller.py +586 -0
  256. chrys/app/tui/screens/main/runtime_info.py +71 -0
  257. chrys/app/tui/screens/main/screen.py +2265 -0
  258. chrys/app/tui/screens/main/screen.tcss +21 -0
  259. chrys/app/tui/screens/main/session_handlers.py +1127 -0
  260. chrys/app/tui/screens/main/session_title.py +239 -0
  261. chrys/app/tui/screens/main/settings_coordinator.py +365 -0
  262. chrys/app/tui/screens/main/settings_persistence.py +184 -0
  263. chrys/app/tui/screens/main/shell_mode.py +112 -0
  264. chrys/app/tui/screens/main/state.py +289 -0
  265. chrys/app/tui/screens/main/submit_state.py +11 -0
  266. chrys/app/tui/screens/main/subscriptions.py +177 -0
  267. chrys/app/tui/screens/main/suggestions.py +982 -0
  268. chrys/app/tui/screens/main/tool_action_bridge.py +75 -0
  269. chrys/app/tui/screens/main/view_adapter.py +1567 -0
  270. chrys/app/tui/screens/main/workflow_browser.py +747 -0
  271. chrys/app/tui/screens/main/workflow_content.py +248 -0
  272. chrys/app/tui/screens/main/workflow_controller.py +240 -0
  273. chrys/app/tui/screens/main/workflow_feedback.py +137 -0
  274. chrys/app/tui/screens/main/workflow_flow.py +49 -0
  275. chrys/app/tui/screens/main/workflow_run_control.py +456 -0
  276. chrys/app/tui/screens/main/workflow_session_view.py +316 -0
  277. chrys/app/tui/screens/main/workspace_actions.py +272 -0
  278. chrys/app/tui/screens/main/workspace_branch.py +194 -0
  279. chrys/app/tui/screens/menus/__init__.py +8 -0
  280. chrys/app/tui/screens/menus/languages.py +114 -0
  281. chrys/app/tui/screens/models/__init__.py +7 -0
  282. chrys/app/tui/screens/models/picker.py +147 -0
  283. chrys/app/tui/screens/models/screen.py +2043 -0
  284. chrys/app/tui/screens/models/screen.tcss +255 -0
  285. chrys/app/tui/screens/sessions/__init__.py +8 -0
  286. chrys/app/tui/screens/sessions/presenter.py +586 -0
  287. chrys/app/tui/screens/sessions/screen.py +779 -0
  288. chrys/app/tui/screens/sessions/screen.tcss +127 -0
  289. chrys/app/tui/screens/settings/__init__.py +29 -0
  290. chrys/app/tui/screens/settings/danger.py +26 -0
  291. chrys/app/tui/screens/settings/dialog.py +281 -0
  292. chrys/app/tui/screens/settings/layout.py +519 -0
  293. chrys/app/tui/screens/settings/migrate_dialog.py +253 -0
  294. chrys/app/tui/screens/settings/panes/__init__.py +3 -0
  295. chrys/app/tui/screens/settings/panes/notifications.py +248 -0
  296. chrys/app/tui/screens/settings/panes/sessions.py +148 -0
  297. chrys/app/tui/screens/settings/ports.py +55 -0
  298. chrys/app/tui/screens/settings/provenance.py +102 -0
  299. chrys/app/tui/screens/settings/rows.py +546 -0
  300. chrys/app/tui/screens/settings/settings.tcss +406 -0
  301. chrys/app/tui/screens/themes/__init__.py +8 -0
  302. chrys/app/tui/screens/themes/dialogs.py +330 -0
  303. chrys/app/tui/screens/themes/editor.py +632 -0
  304. chrys/app/tui/screens/themes/editor.tcss +268 -0
  305. chrys/app/tui/screens/themes/messages.py +95 -0
  306. chrys/app/tui/screens/themes/palette.py +694 -0
  307. chrys/app/tui/screens/themes/panel.py +113 -0
  308. chrys/app/tui/screens/themes/picker.py +203 -0
  309. chrys/app/tui/screens/themes/save.py +191 -0
  310. chrys/app/tui/support/__init__.py +3 -0
  311. chrys/app/tui/support/file_manager.py +64 -0
  312. chrys/app/tui/support/gc_freeze.py +1114 -0
  313. chrys/app/tui/support/path_shortcuts.py +50 -0
  314. chrys/app/tui/support/prompt_history.py +298 -0
  315. chrys/app/tui/support/workspace_mru.py +476 -0
  316. chrys/app/tui/terminal/__init__.py +6 -0
  317. chrys/app/tui/terminal/_pty_backend.py +114 -0
  318. chrys/app/tui/terminal/emulator/__init__.py +24 -0
  319. chrys/app/tui/terminal/emulator/buffer.py +456 -0
  320. chrys/app/tui/terminal/emulator/charsets.py +113 -0
  321. chrys/app/tui/terminal/emulator/core.py +963 -0
  322. chrys/app/tui/terminal/emulator/input.py +342 -0
  323. chrys/app/tui/terminal/emulator/parser.py +227 -0
  324. chrys/app/tui/terminal/emulator/pen.py +158 -0
  325. chrys/app/tui/terminal/launcher.py +455 -0
  326. chrys/app/tui/terminal/leaked_reports.py +44 -0
  327. chrys/app/tui/terminal/panel.py +323 -0
  328. chrys/app/tui/terminal/pty_process.py +405 -0
  329. chrys/app/tui/terminal/rendering.py +151 -0
  330. chrys/app/tui/terminal/shell.py +273 -0
  331. chrys/app/tui/terminal/shell_integration.py +199 -0
  332. chrys/app/tui/terminal/title.py +224 -0
  333. chrys/app/tui/terminal/widget.py +654 -0
  334. chrys/app/tui/theme.py +422 -0
  335. chrys/app/tui/theme_loader.py +201 -0
  336. chrys/app/tui/theme_stylesheet.py +49 -0
  337. chrys/app/tui/themes/__init__.py +3 -0
  338. chrys/app/tui/themes/document.py +120 -0
  339. chrys/app/tui/themes/preview.py +170 -0
  340. chrys/app/tui/themes/store.py +176 -0
  341. chrys/app/tui/toasts.py +69 -0
  342. chrys/app/tui/util/__init__.py +3 -0
  343. chrys/app/tui/util/context_pressure.py +146 -0
  344. chrys/app/tui/util/debug_log.py +292 -0
  345. chrys/app/tui/util/diff_entries.py +64 -0
  346. chrys/app/tui/util/formatting.py +53 -0
  347. chrys/app/tui/util/git_branch.py +326 -0
  348. chrys/app/tui/util/invocation_progress.py +57 -0
  349. chrys/app/tui/util/logo.py +32 -0
  350. chrys/app/tui/util/message_gate.py +35 -0
  351. chrys/app/tui/util/mutation_sources.py +14 -0
  352. chrys/app/tui/util/picker_text.py +27 -0
  353. chrys/app/tui/util/removal.py +47 -0
  354. chrys/app/tui/util/rich_style.py +36 -0
  355. chrys/app/tui/util/source_text.py +12 -0
  356. chrys/app/tui/util/static_update.py +88 -0
  357. chrys/app/tui/util/todo_format.py +51 -0
  358. chrys/app/tui/util/visibility.py +214 -0
  359. chrys/app/tui/widgets/__init__.py +60 -0
  360. chrys/app/tui/widgets/ask_user_controls.py +652 -0
  361. chrys/app/tui/widgets/ask_user_markdown.py +17 -0
  362. chrys/app/tui/widgets/ask_user_prompt.py +606 -0
  363. chrys/app/tui/widgets/buttons.py +52 -0
  364. chrys/app/tui/widgets/chat/__init__.py +3 -0
  365. chrys/app/tui/widgets/chat/agent_transcript_surface.py +818 -0
  366. chrys/app/tui/widgets/chat/chrome.py +296 -0
  367. chrys/app/tui/widgets/chat/compaction_card.py +353 -0
  368. chrys/app/tui/widgets/chat/context_fold.py +77 -0
  369. chrys/app/tui/widgets/chat/file_snapshot.py +91 -0
  370. chrys/app/tui/widgets/chat/image_preview.py +214 -0
  371. chrys/app/tui/widgets/chat/live_renderer.py +383 -0
  372. chrys/app/tui/widgets/chat/messages.py +1039 -0
  373. chrys/app/tui/widgets/chat/panel.py +1769 -0
  374. chrys/app/tui/widgets/chat/ports.py +245 -0
  375. chrys/app/tui/widgets/chat/renderers/__init__.py +7 -0
  376. chrys/app/tui/widgets/chat/renderers/ask_user.py +747 -0
  377. chrys/app/tui/widgets/chat/renderers/execute.py +431 -0
  378. chrys/app/tui/widgets/chat/renderers/file_edit.py +925 -0
  379. chrys/app/tui/widgets/chat/renderers/hosted_code.py +83 -0
  380. chrys/app/tui/widgets/chat/renderers/hosted_generic.py +330 -0
  381. chrys/app/tui/widgets/chat/renderers/hosted_image.py +107 -0
  382. chrys/app/tui/widgets/chat/renderers/hosted_mcp.py +43 -0
  383. chrys/app/tui/widgets/chat/renderers/hosted_search.py +167 -0
  384. chrys/app/tui/widgets/chat/renderers/hosted_shell.py +132 -0
  385. chrys/app/tui/widgets/chat/renderers/read_file.py +399 -0
  386. chrys/app/tui/widgets/chat/renderers/search.py +348 -0
  387. chrys/app/tui/widgets/chat/renderers/skill.py +564 -0
  388. chrys/app/tui/widgets/chat/renderers/sleep.py +287 -0
  389. chrys/app/tui/widgets/chat/renderers/sub_agent.py +1385 -0
  390. chrys/app/tui/widgets/chat/renderers/todo.py +114 -0
  391. chrys/app/tui/widgets/chat/renderers/web.py +69 -0
  392. chrys/app/tui/widgets/chat/replay.py +1605 -0
  393. chrys/app/tui/widgets/chat/replay_mount.py +322 -0
  394. chrys/app/tui/widgets/chat/scroll_controller.py +579 -0
  395. chrys/app/tui/widgets/chat/selection_controller.py +435 -0
  396. chrys/app/tui/widgets/chat/session_json.py +508 -0
  397. chrys/app/tui/widgets/chat/toc_model.py +156 -0
  398. chrys/app/tui/widgets/chat/tool_call.py +2478 -0
  399. chrys/app/tui/widgets/chat/tool_registry.py +753 -0
  400. chrys/app/tui/widgets/chat/tool_renderers.py +247 -0
  401. chrys/app/tui/widgets/chat/tool_view_builders.py +260 -0
  402. chrys/app/tui/widgets/chat/transcript_reader.py +57 -0
  403. chrys/app/tui/widgets/checkbox.py +24 -0
  404. chrys/app/tui/widgets/chrome/__init__.py +3 -0
  405. chrys/app/tui/widgets/chrome/app_header.py +283 -0
  406. chrys/app/tui/widgets/chrome/commands.py +95 -0
  407. chrys/app/tui/widgets/chrome/file_index.py +68 -0
  408. chrys/app/tui/widgets/chrome/file_scanner.py +670 -0
  409. chrys/app/tui/widgets/chrome/footer.py +284 -0
  410. chrys/app/tui/widgets/chrome/image_paste.py +351 -0
  411. chrys/app/tui/widgets/chrome/input_bar.py +1299 -0
  412. chrys/app/tui/widgets/chrome/status_bar.py +911 -0
  413. chrys/app/tui/widgets/chrome/suggestion_list.py +652 -0
  414. chrys/app/tui/widgets/click_affordance.py +24 -0
  415. chrys/app/tui/widgets/color_picker/__init__.py +254 -0
  416. chrys/app/tui/widgets/color_picker/controls.py +171 -0
  417. chrys/app/tui/widgets/color_picker/model.py +89 -0
  418. chrys/app/tui/widgets/dialog_buttons.py +58 -0
  419. chrys/app/tui/widgets/dialog_scroll.py +24 -0
  420. chrys/app/tui/widgets/diff_view/__init__.py +20 -0
  421. chrys/app/tui/widgets/diff_view/cells.py +59 -0
  422. chrys/app/tui/widgets/diff_view/code.py +280 -0
  423. chrys/app/tui/widgets/diff_view/compute.py +286 -0
  424. chrys/app/tui/widgets/diff_view/gutter.py +161 -0
  425. chrys/app/tui/widgets/diff_view/inline.py +77 -0
  426. chrys/app/tui/widgets/diff_view/palette.py +87 -0
  427. chrys/app/tui/widgets/diff_view/protocols.py +24 -0
  428. chrys/app/tui/widgets/diff_view/rows.py +138 -0
  429. chrys/app/tui/widgets/diff_view/unified.py +267 -0
  430. chrys/app/tui/widgets/diff_view/widget.py +209 -0
  431. chrys/app/tui/widgets/editor/__init__.py +79 -0
  432. chrys/app/tui/widgets/editor/coordinates.py +30 -0
  433. chrys/app/tui/widgets/editor/highlighter.py +139 -0
  434. chrys/app/tui/widgets/editor/keymaps.py +357 -0
  435. chrys/app/tui/widgets/editor/types.py +216 -0
  436. chrys/app/tui/widgets/editor/vim.py +1195 -0
  437. chrys/app/tui/widgets/editor/widget.py +401 -0
  438. chrys/app/tui/widgets/file_preview.py +58 -0
  439. chrys/app/tui/widgets/hatch.py +138 -0
  440. chrys/app/tui/widgets/input.py +66 -0
  441. chrys/app/tui/widgets/loading.py +125 -0
  442. chrys/app/tui/widgets/markdown/__init__.py +25 -0
  443. chrys/app/tui/widgets/markdown/_utils.py +21 -0
  444. chrys/app/tui/widgets/markdown/anchors.py +71 -0
  445. chrys/app/tui/widgets/markdown/blocks.py +115 -0
  446. chrys/app/tui/widgets/markdown/copyable.py +163 -0
  447. chrys/app/tui/widgets/markdown/diagram/__init__.py +35 -0
  448. chrys/app/tui/widgets/markdown/diagram/canvas.py +409 -0
  449. chrys/app/tui/widgets/markdown/diagram/charts.py +52 -0
  450. chrys/app/tui/widgets/markdown/diagram/diagnostics.py +101 -0
  451. chrys/app/tui/widgets/markdown/diagram/geometry.py +46 -0
  452. chrys/app/tui/widgets/markdown/diagram/layout.py +494 -0
  453. chrys/app/tui/widgets/markdown/diagram/messages.py +18 -0
  454. chrys/app/tui/widgets/markdown/diagram/model.py +275 -0
  455. chrys/app/tui/widgets/markdown/diagram/parser.py +173 -0
  456. chrys/app/tui/widgets/markdown/diagram/parsers/__init__.py +3 -0
  457. chrys/app/tui/widgets/markdown/diagram/parsers/charts.py +349 -0
  458. chrys/app/tui/widgets/markdown/diagram/parsers/common.py +403 -0
  459. chrys/app/tui/widgets/markdown/diagram/parsers/graphs.py +1072 -0
  460. chrys/app/tui/widgets/markdown/diagram/parsers/planning.py +317 -0
  461. chrys/app/tui/widgets/markdown/diagram/parsers/schedule.py +298 -0
  462. chrys/app/tui/widgets/markdown/diagram/parsers/schemas.py +241 -0
  463. chrys/app/tui/widgets/markdown/diagram/parsers/structure.py +221 -0
  464. chrys/app/tui/widgets/markdown/diagram/presentation.py +32 -0
  465. chrys/app/tui/widgets/markdown/diagram/renderers/__init__.py +3 -0
  466. chrys/app/tui/widgets/markdown/diagram/renderers/charts.py +275 -0
  467. chrys/app/tui/widgets/markdown/diagram/renderers/common.py +73 -0
  468. chrys/app/tui/widgets/markdown/diagram/renderers/planning.py +157 -0
  469. chrys/app/tui/widgets/markdown/diagram/renderers/schedule.py +202 -0
  470. chrys/app/tui/widgets/markdown/diagram/renderers/structure.py +77 -0
  471. chrys/app/tui/widgets/markdown/diagram/renderers/xy.py +385 -0
  472. chrys/app/tui/widgets/markdown/diagram/router.py +651 -0
  473. chrys/app/tui/widgets/markdown/diagram/specs/__init__.py +3 -0
  474. chrys/app/tui/widgets/markdown/diagram/specs/charts.py +94 -0
  475. chrys/app/tui/widgets/markdown/diagram/specs/planning.py +68 -0
  476. chrys/app/tui/widgets/markdown/diagram/specs/schedule.py +65 -0
  477. chrys/app/tui/widgets/markdown/diagram/specs/structure.py +23 -0
  478. chrys/app/tui/widgets/markdown/diagram/viewer.py +92 -0
  479. chrys/app/tui/widgets/markdown/links.py +47 -0
  480. chrys/app/tui/widgets/markdown/math/__init__.py +36 -0
  481. chrys/app/tui/widgets/markdown/math/layout.py +739 -0
  482. chrys/app/tui/widgets/markdown/math/markdown.py +291 -0
  483. chrys/app/tui/widgets/markdown/math/parser.py +325 -0
  484. chrys/app/tui/widgets/markdown/math/symbols.py +325 -0
  485. chrys/app/tui/widgets/markdown/navigator.py +80 -0
  486. chrys/app/tui/widgets/markdown/parser.py +1057 -0
  487. chrys/app/tui/widgets/markdown/stream.py +73 -0
  488. chrys/app/tui/widgets/markdown/toc.py +98 -0
  489. chrys/app/tui/widgets/markdown/viewer.py +146 -0
  490. chrys/app/tui/widgets/markdown/widget.py +2046 -0
  491. chrys/app/tui/widgets/marquee.py +101 -0
  492. chrys/app/tui/widgets/option_menu.py +82 -0
  493. chrys/app/tui/widgets/page_navigator.py +118 -0
  494. chrys/app/tui/widgets/select.py +196 -0
  495. chrys/app/tui/widgets/selection.py +59 -0
  496. chrys/app/tui/widgets/sidebar/__init__.py +3 -0
  497. chrys/app/tui/widgets/sidebar/buddy.py +367 -0
  498. chrys/app/tui/widgets/sidebar/context.py +482 -0
  499. chrys/app/tui/widgets/sidebar/debug.py +195 -0
  500. chrys/app/tui/widgets/sidebar/empty_state.py +11 -0
  501. chrys/app/tui/widgets/sidebar/panel.py +198 -0
  502. chrys/app/tui/widgets/sidebar/tasks.py +198 -0
  503. chrys/app/tui/widgets/sidebar/toc.py +166 -0
  504. chrys/app/tui/widgets/sidebar/toc_types.py +30 -0
  505. chrys/app/tui/widgets/syntax_theme.py +85 -0
  506. chrys/app/tui/widgets/text_area.py +258 -0
  507. chrys/app/tui/widgets/trajectory/__init__.py +8 -0
  508. chrys/app/tui/widgets/trajectory/chartkit.py +264 -0
  509. chrys/app/tui/widgets/trajectory/insights.py +1266 -0
  510. chrys/app/tui/widgets/trajectory/overview.py +1101 -0
  511. chrys/app/tui/widgets/trajectory/panel.py +983 -0
  512. chrys/app/tui/widgets/trajectory/presentation.py +311 -0
  513. chrys/app/tui/widgets/trajectory/session_info.py +124 -0
  514. chrys/app/tui/widgets/trajectory/text_view.py +124 -0
  515. chrys/app/tui/widgets/trajectory/timeline.py +380 -0
  516. chrys/app/tui/widgets/welcome.py +126 -0
  517. chrys/app/tui/widgets/workflow/__init__.py +5 -0
  518. chrys/app/tui/widgets/workflow/edges.py +89 -0
  519. chrys/app/tui/widgets/workflow/graph.py +869 -0
  520. chrys/app/tui/widgets/workflow/info.py +310 -0
  521. chrys/app/tui/widgets/workflow/node_view.py +27 -0
  522. chrys/app/tui/widgets/workflow/output.py +225 -0
  523. chrys/app/tui/widgets/workflow/panel.py +658 -0
  524. chrys/app/tui/widgets/workflow/projector.py +324 -0
  525. chrys/app/tui/widgets/workflow/records.py +69 -0
  526. chrys/app/tui/widgets/workflow/scrollbar.py +82 -0
  527. chrys/app/tui/widgets/workflow/selection.py +151 -0
  528. chrys/app/tui/widgets/workflow/source.py +65 -0
  529. chrys/app/tui/widgets/workflow/text.py +257 -0
  530. chrys/app/tui/widgets/workflow/values.py +317 -0
  531. chrys/foundation/__init__.py +0 -0
  532. chrys/foundation/branding.py +15 -0
  533. chrys/foundation/config/__init__.py +7 -0
  534. chrys/foundation/config/coercion.py +374 -0
  535. chrys/foundation/config/context.py +34 -0
  536. chrys/foundation/config/env_file.py +141 -0
  537. chrys/foundation/config/env_layers.py +370 -0
  538. chrys/foundation/config/migrations.py +521 -0
  539. chrys/foundation/config/notification_events.py +22 -0
  540. chrys/foundation/config/process_settings.py +332 -0
  541. chrys/foundation/config/runtime_pointer.py +133 -0
  542. chrys/foundation/config/settings.py +1952 -0
  543. chrys/foundation/config/settings_store.py +996 -0
  544. chrys/foundation/config/spec.py +396 -0
  545. chrys/foundation/config/user_settings.py +140 -0
  546. chrys/foundation/config/warnings.py +262 -0
  547. chrys/foundation/config/web_values.py +135 -0
  548. chrys/foundation/config/yaml_store.py +487 -0
  549. chrys/foundation/errors/__init__.py +68 -0
  550. chrys/foundation/errors/_legacy.py +117 -0
  551. chrys/foundation/errors/_text.py +13 -0
  552. chrys/foundation/errors/_walk.py +85 -0
  553. chrys/foundation/errors/classify.py +278 -0
  554. chrys/foundation/errors/display.py +274 -0
  555. chrys/foundation/errors/formatting.py +283 -0
  556. chrys/foundation/errors/kinds.py +67 -0
  557. chrys/foundation/errors/network.py +497 -0
  558. chrys/foundation/errors/provider.py +445 -0
  559. chrys/foundation/errors/route.py +85 -0
  560. chrys/foundation/events/__init__.py +8 -0
  561. chrys/foundation/events/bus.py +143 -0
  562. chrys/foundation/events/types.py +1629 -0
  563. chrys/foundation/events/workflow.py +38 -0
  564. chrys/foundation/hosted_tools.py +142 -0
  565. chrys/foundation/i18n/__init__.py +32 -0
  566. chrys/foundation/i18n/_catalogs/zh-Hans/LC_MESSAGES/chrys.mo +0 -0
  567. chrys/foundation/i18n/catalogs.py +42 -0
  568. chrys/foundation/i18n/formatting.py +202 -0
  569. chrys/foundation/i18n/locale.py +92 -0
  570. chrys/foundation/i18n/localizer.py +187 -0
  571. chrys/foundation/i18n/messages.py +268 -0
  572. chrys/foundation/io/__init__.py +3 -0
  573. chrys/foundation/io/result_content.py +237 -0
  574. chrys/foundation/models/__init__.py +3 -0
  575. chrys/foundation/models/approval_reuse.py +23 -0
  576. chrys/foundation/models/ask_user.py +468 -0
  577. chrys/foundation/models/execution.py +18 -0
  578. chrys/foundation/models/history_markers.py +115 -0
  579. chrys/foundation/models/invocations.py +63 -0
  580. chrys/foundation/models/mutation_scope.py +28 -0
  581. chrys/foundation/models/session_env.py +87 -0
  582. chrys/foundation/models/session_surface.py +29 -0
  583. chrys/foundation/models/todos.py +74 -0
  584. chrys/foundation/models/turns.py +125 -0
  585. chrys/foundation/models/workflow_session.py +89 -0
  586. chrys/foundation/models/workspace.py +59 -0
  587. chrys/foundation/net/__init__.py +3 -0
  588. chrys/foundation/net/address_class.py +57 -0
  589. chrys/foundation/net/route_probe.py +94 -0
  590. chrys/foundation/net/url.py +107 -0
  591. chrys/foundation/observability/__init__.py +3 -0
  592. chrys/foundation/observability/exporters.py +174 -0
  593. chrys/foundation/observability/gate.py +31 -0
  594. chrys/foundation/observability/setup.py +497 -0
  595. chrys/foundation/observability/sink.py +228 -0
  596. chrys/foundation/patches/__init__.py +19 -0
  597. chrys/foundation/patches/mdurl_cache.py +59 -0
  598. chrys/foundation/patches/patcher.py +287 -0
  599. chrys/foundation/patches/staged_members.py +205 -0
  600. chrys/foundation/patches/textual_block_border.py +105 -0
  601. chrys/foundation/patches/textual_button_ansi.py +65 -0
  602. chrys/foundation/patches/textual_button_ansi_old.txt +68 -0
  603. chrys/foundation/patches/textual_callback_cache.py +248 -0
  604. chrys/foundation/patches/textual_callback_dispatch.py +109 -0
  605. chrys/foundation/patches/textual_compositor_cjk.py +566 -0
  606. chrys/foundation/patches/textual_dispatch_cache.py +150 -0
  607. chrys/foundation/patches/textual_ime_cursor_anchor.py +92 -0
  608. chrys/foundation/patches/textual_kitty_keyboard.py +268 -0
  609. chrys/foundation/patches/textual_lru_acyclic.py +241 -0
  610. chrys/foundation/patches/textual_message_pump.py +136 -0
  611. chrys/foundation/patches/textual_node_diet.py +875 -0
  612. chrys/foundation/patches/textual_one_shot_timer.py +48 -0
  613. chrys/foundation/patches/textual_option_list.py +90 -0
  614. chrys/foundation/patches/textual_precompose.py +89 -0
  615. chrys/foundation/patches/textual_pruned_tabs.py +69 -0
  616. chrys/foundation/patches/textual_reflow_reuse.py +1753 -0
  617. chrys/foundation/patches/textual_removed_node_caches.py +86 -0
  618. chrys/foundation/patches/textual_removed_screen_callbacks.py +55 -0
  619. chrys/foundation/patches/textual_selection_extract.py +48 -0
  620. chrys/foundation/patches/textual_strip_cycles.py +119 -0
  621. chrys/foundation/patches/textual_tab_selection.py +1611 -0
  622. chrys/foundation/patches/textual_timer_skip.py +70 -0
  623. chrys/foundation/patches/textual_utf8_decoder.py +106 -0
  624. chrys/foundation/patches/textual_win_sleep.py +167 -0
  625. chrys/foundation/patches/watchdog_fsevents.py +70 -0
  626. chrys/foundation/patches/watchdog_windows.py +52 -0
  627. chrys/foundation/platform/__init__.py +556 -0
  628. chrys/foundation/platform/c_api.py +32 -0
  629. chrys/foundation/platform/child_reap.py +107 -0
  630. chrys/foundation/platform/command_line.py +95 -0
  631. chrys/foundation/platform/files.py +1470 -0
  632. chrys/foundation/platform/output_capture.py +113 -0
  633. chrys/foundation/platform/paths.py +193 -0
  634. chrys/foundation/platform/process.py +1468 -0
  635. chrys/foundation/platform/pty_output.py +19 -0
  636. chrys/foundation/platform/runtime_paths.py +281 -0
  637. chrys/foundation/platform/win_console.py +129 -0
  638. chrys/foundation/reasoning_origin.py +67 -0
  639. chrys/foundation/recovery.py +16 -0
  640. chrys/foundation/retry.py +278 -0
  641. chrys/foundation/text/__init__.py +3 -0
  642. chrys/foundation/text/encoding.py +603 -0
  643. chrys/foundation/text/images.py +304 -0
  644. chrys/foundation/text/lines.py +24 -0
  645. chrys/foundation/text/mentions.py +90 -0
  646. chrys/foundation/text/model_json.py +25 -0
  647. chrys/foundation/text/reading_order.py +20 -0
  648. chrys/foundation/text/tokenizer.py +25 -0
  649. chrys/foundation/text/tool_output.py +287 -0
  650. chrys/foundation/text/yaml_io.py +62 -0
  651. chrys/foundation/tool_call_context.py +202 -0
  652. chrys/foundation/tool_execution_stamp.py +184 -0
  653. chrys/foundation/tool_invocation_order.py +48 -0
  654. chrys/foundation/tool_kinds.py +102 -0
  655. chrys/foundation/tool_result_metadata.py +287 -0
  656. chrys/foundation/trajectory/__init__.py +10 -0
  657. chrys/foundation/trajectory/context.py +481 -0
  658. chrys/foundation/trajectory/envelope.py +571 -0
  659. chrys/foundation/trajectory/event_types.py +294 -0
  660. chrys/foundation/trajectory/finalizers.py +46 -0
  661. chrys/foundation/trajectory/fingerprint.py +62 -0
  662. chrys/foundation/trajectory/ids.py +155 -0
  663. chrys/foundation/trajectory/keys.py +99 -0
  664. chrys/foundation/trajectory/lease.py +173 -0
  665. chrys/foundation/trajectory/metadata.py +72 -0
  666. chrys/foundation/trajectory/reader.py +271 -0
  667. chrys/foundation/trajectory/recovery.py +225 -0
  668. chrys/foundation/trajectory/revisions.py +152 -0
  669. chrys/foundation/trajectory/segments.py +225 -0
  670. chrys/foundation/trajectory/tools.py +117 -0
  671. chrys/foundation/trajectory/usage.py +151 -0
  672. chrys/foundation/trajectory/writer.py +972 -0
  673. chrys/foundation/trajectory_timing.py +97 -0
  674. chrys/foundation/util/__init__.py +3 -0
  675. chrys/foundation/util/async_tasks.py +29 -0
  676. chrys/foundation/util/chrys_headers.py +23 -0
  677. chrys/foundation/util/env_templates.py +104 -0
  678. chrys/foundation/util/filenames.py +96 -0
  679. chrys/foundation/util/header_charset.py +138 -0
  680. chrys/foundation/util/httpx_helpers.py +12 -0
  681. chrys/foundation/util/lock.py +236 -0
  682. chrys/foundation/util/once_close.py +46 -0
  683. chrys/foundation/util/session_ids.py +29 -0
  684. chrys/foundation/util/shell_tokens.py +88 -0
  685. chrys/foundation/util/sub_agent_context.py +38 -0
  686. chrys/foundation/util/time.py +35 -0
  687. chrys/foundation/util/unicode_scalars.py +36 -0
  688. chrys/foundation/vendor/__init__.py +43 -0
  689. chrys/foundation/vendor/ripgrep/rg-aarch64-apple-darwin +0 -0
  690. chrys/foundation/vendor/ripgrep/rg-aarch64-pc-windows-msvc.exe +0 -0
  691. chrys/foundation/vendor/ripgrep/rg-aarch64-unknown-linux-gnu +0 -0
  692. chrys/foundation/vendor/ripgrep/rg-x86_64-apple-darwin +0 -0
  693. chrys/foundation/vendor/ripgrep/rg-x86_64-pc-windows-msvc.exe +0 -0
  694. chrys/foundation/vendor/ripgrep/rg-x86_64-unknown-linux-musl +0 -0
  695. chrys/kernel/__init__.py +256 -0
  696. chrys/kernel/_content.py +1866 -0
  697. chrys/kernel/_loop_recorder.py +631 -0
  698. chrys/kernel/_null_overlay.py +1026 -0
  699. chrys/kernel/_result_ceiling.py +216 -0
  700. chrys/kernel/_serialization.py +431 -0
  701. chrys/kernel/_tool_arg_errors.py +1629 -0
  702. chrys/kernel/_tool_execution.py +1172 -0
  703. chrys/kernel/_tool_expansion.py +26 -0
  704. chrys/kernel/_types.py +2598 -0
  705. chrys/kernel/agent.py +994 -0
  706. chrys/kernel/client.py +1084 -0
  707. chrys/kernel/compaction.py +1315 -0
  708. chrys/kernel/exceptions.py +211 -0
  709. chrys/kernel/exchanges.py +683 -0
  710. chrys/kernel/identity.py +95 -0
  711. chrys/kernel/images.py +36 -0
  712. chrys/kernel/instrumentation.py +1618 -0
  713. chrys/kernel/loop.py +2308 -0
  714. chrys/kernel/middleware.py +616 -0
  715. chrys/kernel/sessions.py +604 -0
  716. chrys/kernel/tools.py +1061 -0
  717. chrys/kernel/types.py +126 -0
  718. chrys/orchestration/__init__.py +3 -0
  719. chrys/orchestration/engine/__init__.py +3 -0
  720. chrys/orchestration/engine/assembly.py +236 -0
  721. chrys/orchestration/engine/build/__init__.py +3 -0
  722. chrys/orchestration/engine/build/builder.py +1061 -0
  723. chrys/orchestration/engine/build/construction.py +408 -0
  724. chrys/orchestration/engine/build/loaded.py +139 -0
  725. chrys/orchestration/engine/engine.py +789 -0
  726. chrys/orchestration/engine/execution.py +771 -0
  727. chrys/orchestration/engine/loader.py +865 -0
  728. chrys/orchestration/engine/rollback.py +918 -0
  729. chrys/orchestration/engine/run/__init__.py +3 -0
  730. chrys/orchestration/engine/run/active_injection.py +419 -0
  731. chrys/orchestration/engine/run/attachments.py +519 -0
  732. chrys/orchestration/engine/run/bindings.py +1095 -0
  733. chrys/orchestration/engine/run/coordinator.py +1062 -0
  734. chrys/orchestration/engine/run/finalizer.py +417 -0
  735. chrys/orchestration/engine/run/input_refs.py +60 -0
  736. chrys/orchestration/engine/run/prompt_content.py +457 -0
  737. chrys/orchestration/engine/run/resume.py +299 -0
  738. chrys/orchestration/engine/run/retry.py +948 -0
  739. chrys/orchestration/engine/run/runner.py +625 -0
  740. chrys/orchestration/engine/run/runtime_skills.py +146 -0
  741. chrys/orchestration/engine/run/sub_agent_coordination.py +126 -0
  742. chrys/orchestration/engine/run/turn_hooks.py +258 -0
  743. chrys/orchestration/engine/run/turn_state.py +149 -0
  744. chrys/orchestration/engine/run/working_dir.py +50 -0
  745. chrys/orchestration/engine/session_lifecycle.py +2013 -0
  746. chrys/orchestration/engine/state/__init__.py +3 -0
  747. chrys/orchestration/engine/state/active_session.py +183 -0
  748. chrys/orchestration/engine/state/controls.py +639 -0
  749. chrys/orchestration/engine/state/current_agent.py +26 -0
  750. chrys/orchestration/engine/state/lifecycle_permits.py +395 -0
  751. chrys/orchestration/engine/state/machine.py +193 -0
  752. chrys/orchestration/engine/state/session_writer.py +434 -0
  753. chrys/orchestration/engine/trajectory.py +714 -0
  754. chrys/orchestration/engine/usage.py +118 -0
  755. chrys/orchestration/invoker/__init__.py +3 -0
  756. chrys/orchestration/invoker/acp.py +727 -0
  757. chrys/orchestration/invoker/acp_protocol.py +1115 -0
  758. chrys/orchestration/invoker/acp_spec.py +79 -0
  759. chrys/orchestration/invoker/attempts.py +994 -0
  760. chrys/orchestration/invoker/child_compaction.py +131 -0
  761. chrys/orchestration/invoker/child_history.py +99 -0
  762. chrys/orchestration/invoker/contracts.py +253 -0
  763. chrys/orchestration/invoker/evidence.py +94 -0
  764. chrys/orchestration/invoker/kernel.py +343 -0
  765. chrys/orchestration/invoker/origin.py +79 -0
  766. chrys/orchestration/invoker/resources.py +343 -0
  767. chrys/orchestration/invoker/runtime.py +299 -0
  768. chrys/orchestration/invoker/usage.py +80 -0
  769. chrys/orchestration/session_hooks.py +117 -0
  770. chrys/orchestration/session_host.py +1023 -0
  771. chrys/orchestration/session_resources.py +95 -0
  772. chrys/orchestration/session_usage.py +219 -0
  773. chrys/orchestration/startup.py +255 -0
  774. chrys/orchestration/sub_agents/__init__.py +3 -0
  775. chrys/orchestration/sub_agents/acp_policy.py +269 -0
  776. chrys/orchestration/sub_agents/kernel_policy.py +848 -0
  777. chrys/orchestration/sub_agents/registration.py +44 -0
  778. chrys/orchestration/sub_agents/shell.py +337 -0
  779. chrys/orchestration/sub_agents/tools.py +2228 -0
  780. chrys/orchestration/workflows/__init__.py +3 -0
  781. chrys/orchestration/workflows/agent_archive.py +156 -0
  782. chrys/orchestration/workflows/agent_node.py +894 -0
  783. chrys/orchestration/workflows/agent_node_build.py +687 -0
  784. chrys/orchestration/workflows/catalog.py +332 -0
  785. chrys/orchestration/workflows/coordinator.py +771 -0
  786. chrys/orchestration/workflows/hooks.py +176 -0
  787. chrys/orchestration/workflows/preview.py +322 -0
  788. chrys/orchestration/workflows/rollback.py +151 -0
  789. chrys/orchestration/workflows/runner.py +986 -0
  790. chrys/orchestration/workflows/session.py +390 -0
  791. chrys/orchestration/workflows/settings.py +97 -0
  792. chrys/orchestration/workflows/validation.py +674 -0
  793. chrys/orchestration/workflows/worker_client.py +1002 -0
  794. chrys/service/__init__.py +3 -0
  795. chrys/service/acp_client/__init__.py +65 -0
  796. chrys/service/acp_client/_transport.py +922 -0
  797. chrys/service/acp_client/client.py +1311 -0
  798. chrys/service/acp_client/errors.py +224 -0
  799. chrys/service/acp_client/protocol.py +220 -0
  800. chrys/service/acp_client/spawn.py +239 -0
  801. chrys/service/acp_client/spec.py +100 -0
  802. chrys/service/agent_middleware/__init__.py +60 -0
  803. chrys/service/agent_middleware/_metadata_keys.py +52 -0
  804. chrys/service/agent_middleware/control/__init__.py +3 -0
  805. chrys/service/agent_middleware/control/approval.py +769 -0
  806. chrys/service/agent_middleware/control/approval_hooks.py +62 -0
  807. chrys/service/agent_middleware/control/ask_user.py +207 -0
  808. chrys/service/agent_middleware/control/interrupt.py +50 -0
  809. chrys/service/agent_middleware/control/sleep.py +118 -0
  810. chrys/service/agent_middleware/control/todo.py +117 -0
  811. chrys/service/agent_middleware/events/__init__.py +3 -0
  812. chrys/service/agent_middleware/events/hook_dispatch.py +250 -0
  813. chrys/service/agent_middleware/events/hosted_tools.py +1762 -0
  814. chrys/service/agent_middleware/events/intermediate_text.py +128 -0
  815. chrys/service/agent_middleware/events/rejection_metadata.py +50 -0
  816. chrys/service/agent_middleware/events/result_persistence.py +98 -0
  817. chrys/service/agent_middleware/events/sub_agent_events.py +951 -0
  818. chrys/service/agent_middleware/events/tool_error_text.py +22 -0
  819. chrys/service/agent_middleware/events/tool_events.py +779 -0
  820. chrys/service/agent_middleware/injection.py +510 -0
  821. chrys/service/agent_middleware/reminders/__init__.py +91 -0
  822. chrys/service/agent_middleware/reminders/archive_pointer.py +154 -0
  823. chrys/service/agent_middleware/reminders/base.py +25 -0
  824. chrys/service/agent_middleware/reminders/context_usage.py +74 -0
  825. chrys/service/agent_middleware/reminders/file_change.py +63 -0
  826. chrys/service/agent_middleware/reminders/mcp.py +39 -0
  827. chrys/service/agent_middleware/reminders/profile_switch.py +106 -0
  828. chrys/service/agent_middleware/reminders/runtime_env.py +190 -0
  829. chrys/service/agent_middleware/reminders/skills.py +44 -0
  830. chrys/service/agent_middleware/reminders/sub_agents.py +35 -0
  831. chrys/service/agent_middleware/reminders/todo.py +35 -0
  832. chrys/service/agent_middleware/reminders/turn_line.py +48 -0
  833. chrys/service/agent_middleware/response_validation.py +1380 -0
  834. chrys/service/agent_middleware/system_reminder.py +1107 -0
  835. chrys/service/agent_middleware/validators.py +396 -0
  836. chrys/service/analytics/__init__.py +124 -0
  837. chrys/service/analytics/_actions.py +896 -0
  838. chrys/service/analytics/_context_evidence.py +357 -0
  839. chrys/service/analytics/_critical_path.py +179 -0
  840. chrys/service/analytics/_facts.py +919 -0
  841. chrys/service/analytics/_insights.py +769 -0
  842. chrys/service/analytics/_metric_ops.py +83 -0
  843. chrys/service/analytics/_session_projection.py +460 -0
  844. chrys/service/analytics/_timeline.py +434 -0
  845. chrys/service/analytics/_turn_graph.py +1237 -0
  846. chrys/service/analytics/_turns.py +1377 -0
  847. chrys/service/analytics/_workflows.py +171 -0
  848. chrys/service/analytics/aggregation.py +1474 -0
  849. chrys/service/analytics/classification.py +128 -0
  850. chrys/service/analytics/export.py +803 -0
  851. chrys/service/analytics/findings.py +282 -0
  852. chrys/service/analytics/math.py +79 -0
  853. chrys/service/analytics/model.py +886 -0
  854. chrys/service/analytics/reader.py +321 -0
  855. chrys/service/approval/__init__.py +3 -0
  856. chrys/service/approval/arbitration.py +102 -0
  857. chrys/service/approval/command_identity.py +109 -0
  858. chrys/service/approval/correlation.py +57 -0
  859. chrys/service/approval/grant_store.py +108 -0
  860. chrys/service/approval/judge.py +589 -0
  861. chrys/service/approval/level.py +15 -0
  862. chrys/service/approval/policy.py +209 -0
  863. chrys/service/approval/reuse.py +223 -0
  864. chrys/service/approval/reuse_binding.py +133 -0
  865. chrys/service/approval/safety_classifier.py +536 -0
  866. chrys/service/approval/turn_context.py +49 -0
  867. chrys/service/context/__init__.py +3 -0
  868. chrys/service/context/compaction/__init__.py +38 -0
  869. chrys/service/context/compaction/breaker.py +156 -0
  870. chrys/service/context/compaction/budgets.py +93 -0
  871. chrys/service/context/compaction/calibration.py +77 -0
  872. chrys/service/context/compaction/compression.py +690 -0
  873. chrys/service/context/compaction/current_turn_drop.py +458 -0
  874. chrys/service/context/compaction/events.py +201 -0
  875. chrys/service/context/compaction/exclusions.py +261 -0
  876. chrys/service/context/compaction/groups.py +361 -0
  877. chrys/service/context/compaction/last_words.py +1996 -0
  878. chrys/service/context/compaction/last_words_state.py +752 -0
  879. chrys/service/context/compaction/scoped.py +472 -0
  880. chrys/service/context/compaction/spill.py +1947 -0
  881. chrys/service/context/compaction/strategy.py +1116 -0
  882. chrys/service/context/compaction/summaries.py +319 -0
  883. chrys/service/context/compaction/turns.py +375 -0
  884. chrys/service/context/manager.py +174 -0
  885. chrys/service/context/memory_loader.py +792 -0
  886. chrys/service/context/middleware/__init__.py +3 -0
  887. chrys/service/context/middleware/usage.py +332 -0
  888. chrys/service/context/providers/__init__.py +3 -0
  889. chrys/service/context/providers/context_management.py +432 -0
  890. chrys/service/context/providers/history.py +647 -0
  891. chrys/service/context/providers/memory.py +50 -0
  892. chrys/service/context/tool_content.py +22 -0
  893. chrys/service/hooks/__init__.py +37 -0
  894. chrys/service/hooks/detached_worker.py +118 -0
  895. chrys/service/hooks/docs/README.md +14 -0
  896. chrys/service/hooks/docs/authoring.md +684 -0
  897. chrys/service/hooks/docs/configuration.md +499 -0
  898. chrys/service/hooks/docs/design.md +359 -0
  899. chrys/service/hooks/docs/project-hooks.md +348 -0
  900. chrys/service/hooks/events.py +157 -0
  901. chrys/service/hooks/loader.py +665 -0
  902. chrys/service/hooks/manager.py +807 -0
  903. chrys/service/hooks/matcher.py +111 -0
  904. chrys/service/hooks/outbox.py +294 -0
  905. chrys/service/hooks/runner.py +572 -0
  906. chrys/service/hooks/schema.py +407 -0
  907. chrys/service/llm/__init__.py +7 -0
  908. chrys/service/llm/_structured_outputs.py +192 -0
  909. chrys/service/llm/anthropic_messages/__init__.py +9 -0
  910. chrys/service/llm/anthropic_messages/client.py +254 -0
  911. chrys/service/llm/anthropic_messages/decode.py +331 -0
  912. chrys/service/llm/anthropic_messages/history.py +279 -0
  913. chrys/service/llm/anthropic_messages/request.py +323 -0
  914. chrys/service/llm/anthropic_messages/server_tools.py +319 -0
  915. chrys/service/llm/anthropic_messages/stream.py +297 -0
  916. chrys/service/llm/anthropic_messages/thinking_binding.py +156 -0
  917. chrys/service/llm/chat_completions/__init__.py +9 -0
  918. chrys/service/llm/chat_completions/client.py +369 -0
  919. chrys/service/llm/chat_completions/decode.py +338 -0
  920. chrys/service/llm/chat_completions/history.py +657 -0
  921. chrys/service/llm/chat_completions/reasoning.py +244 -0
  922. chrys/service/llm/chat_completions/request.py +192 -0
  923. chrys/service/llm/chat_completions/stream.py +546 -0
  924. chrys/service/llm/chat_completions/validation.py +308 -0
  925. chrys/service/llm/clients.py +613 -0
  926. chrys/service/llm/images.py +65 -0
  927. chrys/service/llm/mock.py +413 -0
  928. chrys/service/llm/observer.py +446 -0
  929. chrys/service/llm/one_shot.py +106 -0
  930. chrys/service/llm/openai_exceptions.py +93 -0
  931. chrys/service/llm/openai_responses/__init__.py +10 -0
  932. chrys/service/llm/openai_responses/client.py +323 -0
  933. chrys/service/llm/openai_responses/decode.py +432 -0
  934. chrys/service/llm/openai_responses/history.py +519 -0
  935. chrys/service/llm/openai_responses/hosted.py +625 -0
  936. chrys/service/llm/openai_responses/replay.py +629 -0
  937. chrys/service/llm/openai_responses/request.py +342 -0
  938. chrys/service/llm/openai_responses/stream.py +847 -0
  939. chrys/service/llm/openai_timestamps.py +57 -0
  940. chrys/service/llm/providers.py +107 -0
  941. chrys/service/llm/proxy_route.py +54 -0
  942. chrys/service/llm/raw_http_log.py +309 -0
  943. chrys/service/llm/route_facts.py +63 -0
  944. chrys/service/llm/route_sessions.py +49 -0
  945. chrys/service/llm/sdk_anthropic.py +18 -0
  946. chrys/service/llm/sdk_headers.py +47 -0
  947. chrys/service/llm/sdk_openai.py +18 -0
  948. chrys/service/llm/sdk_retry.py +59 -0
  949. chrys/service/llm/wire_client.py +246 -0
  950. chrys/service/mcp/__init__.py +3 -0
  951. chrys/service/mcp/_connection.py +235 -0
  952. chrys/service/mcp/_http_transport.py +350 -0
  953. chrys/service/mcp/_progressive.py +459 -0
  954. chrys/service/mcp/_stdio_transport.py +614 -0
  955. chrys/service/mcp/_tool_mixins.py +96 -0
  956. chrys/service/mcp/adapter.py +1031 -0
  957. chrys/service/mcp/cache.py +476 -0
  958. chrys/service/mcp/content.py +32 -0
  959. chrys/service/mcp/errors.py +169 -0
  960. chrys/service/mcp/owned.py +1330 -0
  961. chrys/service/mcp/result_limits.py +168 -0
  962. chrys/service/mcp/thinking_warning.py +95 -0
  963. chrys/service/mcp/validation.py +74 -0
  964. chrys/service/mutations/__init__.py +84 -0
  965. chrys/service/mutations/coordination.py +894 -0
  966. chrys/service/mutations/detector.py +458 -0
  967. chrys/service/mutations/git_calibrator.py +397 -0
  968. chrys/service/mutations/git_state.py +647 -0
  969. chrys/service/mutations/pipeline.py +779 -0
  970. chrys/service/mutations/scanner.py +466 -0
  971. chrys/service/mutations/snapshot_files.py +233 -0
  972. chrys/service/mutations/store.py +625 -0
  973. chrys/service/mutations/tool_names.py +15 -0
  974. chrys/service/mutations/trace.py +246 -0
  975. chrys/service/mutations/tracker.py +1289 -0
  976. chrys/service/mutations/types.py +559 -0
  977. chrys/service/mutations/workspace_changes.py +1583 -0
  978. chrys/service/profiles/__init__.py +2 -0
  979. chrys/service/profiles/agents/__init__.py +3 -0
  980. chrys/service/profiles/agents/builtins/Code.yaml +199 -0
  981. chrys/service/profiles/agents/builtins/Explore.yaml +62 -0
  982. chrys/service/profiles/agents/builtins/General.yaml +114 -0
  983. chrys/service/profiles/agents/builtins/QA.yaml +128 -0
  984. chrys/service/profiles/agents/loader.py +808 -0
  985. chrys/service/profiles/agents/registry.py +727 -0
  986. chrys/service/profiles/agents/schema.py +371 -0
  987. chrys/service/profiles/agents/serializer.py +196 -0
  988. chrys/service/profiles/coercion.py +22 -0
  989. chrys/service/profiles/models/__init__.py +3 -0
  990. chrys/service/profiles/models/env_bridge.py +157 -0
  991. chrys/service/profiles/models/loader.py +282 -0
  992. chrys/service/profiles/models/options.py +438 -0
  993. chrys/service/profiles/models/registry.py +104 -0
  994. chrys/service/profiles/models/resolver.py +286 -0
  995. chrys/service/profiles/models/schema.py +91 -0
  996. chrys/service/profiles/models/serializer.py +76 -0
  997. chrys/service/session/__init__.py +3 -0
  998. chrys/service/session/checkpoint.py +156 -0
  999. chrys/service/session/history.py +1616 -0
  1000. chrys/service/session/message_metadata.py +151 -0
  1001. chrys/service/session/persistence.py +435 -0
  1002. chrys/service/session/runtime_metadata.py +210 -0
  1003. chrys/service/session/sub_agent_logs.py +1069 -0
  1004. chrys/service/session/sub_agent_transcript.py +654 -0
  1005. chrys/service/skills/__init__.py +3 -0
  1006. chrys/service/skills/adapter.py +408 -0
  1007. chrys/service/skills/constants.py +8 -0
  1008. chrys/service/skills/loader.py +575 -0
  1009. chrys/service/skills/model.py +130 -0
  1010. chrys/service/skills/provider.py +841 -0
  1011. chrys/service/skills/runner.py +390 -0
  1012. chrys/service/state/__init__.py +3 -0
  1013. chrys/service/state/_fork.py +710 -0
  1014. chrys/service/state/_session_files.py +175 -0
  1015. chrys/service/state/_session_meta.py +552 -0
  1016. chrys/service/state/locks.py +154 -0
  1017. chrys/service/state/serializers.py +156 -0
  1018. chrys/service/state/session_catalog.py +361 -0
  1019. chrys/service/state/session_listing.py +84 -0
  1020. chrys/service/state/session_migration.py +446 -0
  1021. chrys/service/state/session_mru.py +315 -0
  1022. chrys/service/state/store.py +2132 -0
  1023. chrys/service/state/workflow.py +158 -0
  1024. chrys/service/text_blocks.py +47 -0
  1025. chrys/service/todos/__init__.py +3 -0
  1026. chrys/service/todos/reminder.py +31 -0
  1027. chrys/service/todos/tracker.py +58 -0
  1028. chrys/service/tools/__init__.py +3 -0
  1029. chrys/service/tools/approval_targets.py +92 -0
  1030. chrys/service/tools/builtins/__init__.py +3 -0
  1031. chrys/service/tools/builtins/ask_user.py +326 -0
  1032. chrys/service/tools/builtins/doc_converter/__init__.py +494 -0
  1033. chrys/service/tools/builtins/doc_converter/artifacts.py +392 -0
  1034. chrys/service/tools/builtins/doc_converter/parsers/__init__.py +3 -0
  1035. chrys/service/tools/builtins/doc_converter/parsers/_table.py +34 -0
  1036. chrys/service/tools/builtins/doc_converter/parsers/base.py +101 -0
  1037. chrys/service/tools/builtins/doc_converter/parsers/docx.py +88 -0
  1038. chrys/service/tools/builtins/doc_converter/parsers/pdf.py +144 -0
  1039. chrys/service/tools/builtins/doc_converter/parsers/pptx.py +103 -0
  1040. chrys/service/tools/builtins/doc_converter/parsers/xls.py +32 -0
  1041. chrys/service/tools/builtins/doc_converter/parsers/xlsx.py +51 -0
  1042. chrys/service/tools/builtins/doc_converter/registry.py +55 -0
  1043. chrys/service/tools/builtins/filesystem.py +1071 -0
  1044. chrys/service/tools/builtins/search.py +1265 -0
  1045. chrys/service/tools/builtins/shell.py +967 -0
  1046. chrys/service/tools/builtins/shell_command_filtering/__init__.py +5 -0
  1047. chrys/service/tools/builtins/shell_command_filtering/policy.py +60 -0
  1048. chrys/service/tools/builtins/shell_command_filtering/presets.py +152 -0
  1049. chrys/service/tools/builtins/shell_command_filtering/readonly/__init__.py +358 -0
  1050. chrys/service/tools/builtins/shell_command_filtering/readonly/commands.py +416 -0
  1051. chrys/service/tools/builtins/shell_command_filtering/readonly/git.py +408 -0
  1052. chrys/service/tools/builtins/shell_command_filtering/readonly/syntax.py +87 -0
  1053. chrys/service/tools/builtins/shell_command_filtering/tokenizer.py +214 -0
  1054. chrys/service/tools/builtins/shell_filter.py +24 -0
  1055. chrys/service/tools/builtins/sleep.py +91 -0
  1056. chrys/service/tools/builtins/todo.py +64 -0
  1057. chrys/service/tools/builtins/web/__init__.py +6 -0
  1058. chrys/service/tools/builtins/web/build.py +268 -0
  1059. chrys/service/tools/builtins/web/config.py +338 -0
  1060. chrys/service/tools/builtins/web/credentials.py +37 -0
  1061. chrys/service/tools/builtins/web/fetch/__init__.py +3 -0
  1062. chrys/service/tools/builtins/web/fetch/cache.py +68 -0
  1063. chrys/service/tools/builtins/web/fetch/convert.py +56 -0
  1064. chrys/service/tools/builtins/web/fetch/tool.py +223 -0
  1065. chrys/service/tools/builtins/web/hosted.py +61 -0
  1066. chrys/service/tools/builtins/web/http.py +330 -0
  1067. chrys/service/tools/builtins/web/progress.py +18 -0
  1068. chrys/service/tools/builtins/web/search/__init__.py +3 -0
  1069. chrys/service/tools/builtins/web/search/exa_mcp.py +146 -0
  1070. chrys/service/tools/builtins/web/search/html.py +152 -0
  1071. chrys/service/tools/builtins/web/search/json_api.py +227 -0
  1072. chrys/service/tools/builtins/web/search/providers.py +77 -0
  1073. chrys/service/tools/builtins/web/search/tool.py +218 -0
  1074. chrys/service/tools/builtins/web/search/types.py +28 -0
  1075. chrys/service/tools/builtins/web/tls_identity.py +66 -0
  1076. chrys/service/tools/kinds.py +115 -0
  1077. chrys/service/tools/names.py +47 -0
  1078. chrys/service/tools/registry.py +242 -0
  1079. chrys/service/tools/result_metadata.py +103 -0
  1080. chrys/service/tools/session_artifacts.py +196 -0
  1081. chrys/service/tools/spill.py +218 -0
  1082. chrys/service/tools/workspace_paths.py +81 -0
  1083. chrys/service/trajectory/__init__.py +11 -0
  1084. chrys/service/trajectory/approvals.py +201 -0
  1085. chrys/service/trajectory/compaction.py +420 -0
  1086. chrys/service/trajectory/fork.py +75 -0
  1087. chrys/service/trajectory/hooks.py +217 -0
  1088. chrys/service/trajectory/items.py +41 -0
  1089. chrys/service/trajectory/preparation.py +374 -0
  1090. chrys/service/trajectory/retries.py +95 -0
  1091. chrys/service/trajectory/revisions.py +129 -0
  1092. chrys/service/trajectory/segmented.py +163 -0
  1093. chrys/service/trajectory/session.py +762 -0
  1094. chrys/service/trajectory/state.py +83 -0
  1095. chrys/service/trajectory/sub_agents.py +169 -0
  1096. chrys/service/trajectory/tombstone.py +262 -0
  1097. chrys/service/trajectory/tools.py +323 -0
  1098. chrys/service/trajectory/validation.py +130 -0
  1099. chrys/service/trajectory/waits.py +161 -0
  1100. chrys/service/trajectory/workflow.py +148 -0
  1101. chrys/service/vision.py +128 -0
  1102. chrys/service/workflows/__init__.py +3 -0
  1103. chrys/service/workflows/admission.py +151 -0
  1104. chrys/service/workflows/artifacts.py +109 -0
  1105. chrys/service/workflows/asks.py +130 -0
  1106. chrys/service/workflows/builtins/__init__.py +12 -0
  1107. chrys/service/workflows/builtins/demo-workflow.manifest.json +635 -0
  1108. chrys/service/workflows/builtins/demo-workflow.py +756 -0
  1109. chrys/service/workflows/discovery.py +554 -0
  1110. chrys/service/workflows/environment.py +284 -0
  1111. chrys/service/workflows/graph.py +430 -0
  1112. chrys/service/workflows/history.py +82 -0
  1113. chrys/service/workflows/interpreter.py +107 -0
  1114. chrys/service/workflows/journal.py +334 -0
  1115. chrys/service/workflows/layout.py +56 -0
  1116. chrys/service/workflows/ledger.py +149 -0
  1117. chrys/service/workflows/model_selection.py +18 -0
  1118. chrys/service/workflows/orphans.py +209 -0
  1119. chrys/service/workflows/outcomes.py +46 -0
  1120. chrys/service/workflows/protocol.py +210 -0
  1121. chrys/service/workflows/records.py +142 -0
  1122. chrys/service/workflows/scheduler.py +770 -0
  1123. chrys/service/workflows/sdk/__init__.py +51 -0
  1124. chrys/service/workflows/sdk/_ask.py +167 -0
  1125. chrys/service/workflows/sdk/_builder.py +781 -0
  1126. chrys/service/workflows/sdk/_values.py +92 -0
  1127. chrys/service/workflows/sdk_artifact.py +74 -0
  1128. chrys/service/workflows/store.py +494 -0
  1129. chrys/service/workflows/transcript.py +102 -0
  1130. chrys/service/workflows/values.py +73 -0
  1131. chrys/service/workflows/worker_host.py +1820 -0
  1132. chrys/workflows.py +38 -0
  1133. icode_tui-0.29.1.dist-info/METADATA +104 -0
  1134. icode_tui-0.29.1.dist-info/RECORD +1138 -0
  1135. icode_tui-0.29.1.dist-info/WHEEL +4 -0
  1136. icode_tui-0.29.1.dist-info/entry_points.txt +2 -0
  1137. icode_tui-0.29.1.dist-info/licenses/LICENSE +202 -0
  1138. icode_tui-0.29.1.dist-info/licenses/NOTICE +1955 -0
@@ -0,0 +1,1195 @@
1
+ # Copyright (c) Huawei Technologies Co., Ltd. 2026. All rights reserved.
2
+
3
+ """Pure Vim keymap state machine for the modal message editor."""
4
+
5
+ from __future__ import annotations
6
+
7
+ from dataclasses import dataclass
8
+ from enum import StrEnum
9
+ from types import MappingProxyType
10
+ from typing import Final
11
+
12
+ from chrys.app.tui.widgets.editor.coordinates import location_to_offset, offset_to_location
13
+ from chrys.app.tui.widgets.editor.types import (
14
+ COMMON_EDITOR_INTENTS,
15
+ MESSAGE_EDITOR_CHARACTER_LIMIT_STATUS,
16
+ MESSAGE_EDITOR_MAX_CHARACTERS,
17
+ EditorCommand,
18
+ EditorCommandHost,
19
+ EditorIntent,
20
+ EditorKeyResult,
21
+ EditorKeyStroke,
22
+ EditorLocation,
23
+ EditorMode,
24
+ EditorRegister,
25
+ EditorRegisterKind,
26
+ EditorStatus,
27
+ EditorTextRange,
28
+ )
29
+ from chrys.foundation.i18n import msg
30
+
31
+ MAX_VIM_COUNT: Final = 9_999
32
+
33
+ _VIM_MODE_NORMAL = msg("tui.editor.vim.mode.normal", fallback="Normal")
34
+ _VIM_MODE_INSERT = msg("tui.editor.vim.mode.insert", fallback="Insert")
35
+ _VIM_MODE_VISUAL = msg("tui.editor.vim.mode.visual", fallback="Visual")
36
+ _VIM_MODE_VISUAL_LINE = msg("tui.editor.vim.mode.visual_line", fallback="Visual Line")
37
+ _VIM_NO_WRITE = msg(
38
+ "tui.editor.vim.no_write_since_change",
39
+ fallback="No write since last change; use :q!",
40
+ )
41
+ _VIM_NOT_EDITOR_COMMAND = msg(
42
+ "tui.editor.vim.not_editor_command",
43
+ fallback="Not an editor command: {command}",
44
+ )
45
+
46
+
47
+ class VimState(StrEnum):
48
+ """Finite states supported by the v1 Vim keymap."""
49
+
50
+ NORMAL = "normal"
51
+ INSERT = "insert"
52
+ VISUAL_CHAR = "visual_char"
53
+ VISUAL_LINE = "visual_line"
54
+ OPERATOR_PENDING = "operator_pending"
55
+ TEXT_OBJECT_PENDING = "text_object_pending"
56
+ REPLACE_PENDING = "replace_pending"
57
+ COMMAND = "command"
58
+
59
+
60
+ class VimOperator(StrEnum):
61
+ """Supported Vim operators."""
62
+
63
+ DELETE = "d"
64
+ CHANGE = "c"
65
+ YANK = "y"
66
+
67
+
68
+ class VimMotion(StrEnum):
69
+ """Motions accepted in Normal, Visual, and operator-pending states."""
70
+
71
+ LEFT = "h"
72
+ RIGHT = "l"
73
+ DOWN = "j"
74
+ UP = "k"
75
+ LINE_START = "0"
76
+ FIRST_NON_BLANK = "^"
77
+ LINE_END = "$"
78
+ WORD_NEXT = "w"
79
+ WORD_PREVIOUS = "b"
80
+ WORD_END = "e"
81
+ DOCUMENT_START = "gg"
82
+ DOCUMENT_END = "G"
83
+
84
+
85
+ class VimTextObject(StrEnum):
86
+ """Text objects supported by v1."""
87
+
88
+ WORD = "w"
89
+ WORD_BIG = "W"
90
+ DOUBLE_QUOTE = '"'
91
+ SINGLE_QUOTE = "'"
92
+ BACKTICK = "`"
93
+ PAREN = "("
94
+ BRACKET = "["
95
+ BRACE = "{"
96
+
97
+
98
+ @dataclass(frozen=True, slots=True)
99
+ class _MotionTarget:
100
+ location: EditorLocation
101
+ inclusive: bool = False
102
+ linewise: bool = False
103
+
104
+
105
+ _MOTIONS = MappingProxyType(
106
+ {
107
+ "h": VimMotion.LEFT,
108
+ "left": VimMotion.LEFT,
109
+ "l": VimMotion.RIGHT,
110
+ "right": VimMotion.RIGHT,
111
+ "j": VimMotion.DOWN,
112
+ "down": VimMotion.DOWN,
113
+ "k": VimMotion.UP,
114
+ "up": VimMotion.UP,
115
+ "0": VimMotion.LINE_START,
116
+ "^": VimMotion.FIRST_NON_BLANK,
117
+ "circumflex_accent": VimMotion.FIRST_NON_BLANK,
118
+ "$": VimMotion.LINE_END,
119
+ "dollar_sign": VimMotion.LINE_END,
120
+ "w": VimMotion.WORD_NEXT,
121
+ "b": VimMotion.WORD_PREVIOUS,
122
+ "e": VimMotion.WORD_END,
123
+ "G": VimMotion.DOCUMENT_END,
124
+ }
125
+ )
126
+
127
+ _OPERATOR_MOTIONS = frozenset(
128
+ {
129
+ VimMotion.LEFT,
130
+ VimMotion.RIGHT,
131
+ VimMotion.LINE_START,
132
+ VimMotion.FIRST_NON_BLANK,
133
+ VimMotion.LINE_END,
134
+ VimMotion.WORD_NEXT,
135
+ VimMotion.WORD_PREVIOUS,
136
+ VimMotion.WORD_END,
137
+ VimMotion.DOCUMENT_END,
138
+ }
139
+ )
140
+
141
+ _OPERATORS = MappingProxyType(
142
+ {
143
+ "d": VimOperator.DELETE,
144
+ "c": VimOperator.CHANGE,
145
+ "y": VimOperator.YANK,
146
+ }
147
+ )
148
+
149
+ _INSERT_COMMANDS = frozenset({"i", "I", "a", "A", "o", "O"})
150
+ _ALIASES = MappingProxyType(
151
+ {
152
+ "D": (VimOperator.DELETE, VimMotion.LINE_END, False),
153
+ "C": (VimOperator.CHANGE, VimMotion.LINE_END, False),
154
+ "Y": (VimOperator.YANK, None, True),
155
+ "S": (VimOperator.CHANGE, None, True),
156
+ "s": (VimOperator.CHANGE, VimMotion.RIGHT, False),
157
+ }
158
+ )
159
+
160
+ _TEXT_OBJECTS = MappingProxyType(
161
+ {
162
+ "w": VimTextObject.WORD,
163
+ "W": VimTextObject.WORD_BIG,
164
+ '"': VimTextObject.DOUBLE_QUOTE,
165
+ "'": VimTextObject.SINGLE_QUOTE,
166
+ "`": VimTextObject.BACKTICK,
167
+ "(": VimTextObject.PAREN,
168
+ "b": VimTextObject.PAREN,
169
+ "[": VimTextObject.BRACKET,
170
+ "{": VimTextObject.BRACE,
171
+ "B": VimTextObject.BRACE,
172
+ }
173
+ )
174
+
175
+
176
+ def _lines(text: str) -> list[str]:
177
+ return text.split("\n")
178
+
179
+
180
+ def _normal_location(text: str, location: EditorLocation) -> EditorLocation:
181
+ lines = _lines(text)
182
+ row = min(max(location[0], 0), len(lines) - 1)
183
+ line = lines[row]
184
+ return row, min(max(location[1], 0), max(len(line) - 1, 0))
185
+
186
+
187
+ def _first_non_blank(line: str) -> int:
188
+ return len(line) - len(line.lstrip())
189
+
190
+
191
+ def _is_word(character: str) -> bool:
192
+ return character.isalnum() or character == "_"
193
+
194
+
195
+ def _token_class(character: str, *, big: bool = False) -> int:
196
+ if character.isspace():
197
+ return 0
198
+ if big or _is_word(character):
199
+ return 1
200
+ return 2
201
+
202
+
203
+ def _next_word_start(text: str, offset: int, count: int) -> int:
204
+ index = min(max(offset, 0), len(text))
205
+ for _ in range(count):
206
+ if index >= len(text):
207
+ break
208
+ character_class = _token_class(text[index])
209
+ while index < len(text) and _token_class(text[index]) == character_class and character_class != 0:
210
+ index += 1
211
+ while index < len(text) and text[index].isspace():
212
+ index += 1
213
+ if character_class == 0 and index < len(text):
214
+ continue
215
+ return index
216
+
217
+
218
+ def _previous_word_start(text: str, offset: int, count: int) -> int:
219
+ index = min(max(offset, 0), len(text))
220
+ for _ in range(count):
221
+ index = max(index - 1, 0)
222
+ while index > 0 and text[index].isspace():
223
+ index -= 1
224
+ character_class = _token_class(text[index]) if text else 0
225
+ while index > 0 and _token_class(text[index - 1]) == character_class and character_class != 0:
226
+ index -= 1
227
+ return index
228
+
229
+
230
+ def _word_end(text: str, offset: int, count: int) -> int:
231
+ if not text:
232
+ return 0
233
+ index = min(max(offset, 0), len(text) - 1)
234
+ for _ in range(count):
235
+ character_class = _token_class(text[index])
236
+ has_later_character_in_token = (
237
+ character_class != 0 and index < len(text) - 1 and _token_class(text[index + 1]) == character_class
238
+ )
239
+ if not has_later_character_in_token:
240
+ if index < len(text) - 1:
241
+ index += 1
242
+ while index < len(text) - 1 and text[index].isspace():
243
+ index += 1
244
+ character_class = _token_class(text[index])
245
+ while index < len(text) - 1 and _token_class(text[index + 1]) == character_class and character_class != 0:
246
+ index += 1
247
+ return index
248
+
249
+
250
+ def _advance_location(text: str, location: EditorLocation, amount: int = 1) -> EditorLocation:
251
+ return offset_to_location(text, location_to_offset(text, location) + amount)
252
+
253
+
254
+ def _range_from_offsets(text: str, start: int, end: int) -> EditorTextRange:
255
+ low, high = sorted((start, end))
256
+ return EditorTextRange(offset_to_location(text, low), offset_to_location(text, high))
257
+
258
+
259
+ class VimKeymap:
260
+ """Vim v1 state machine with one unnamed register."""
261
+
262
+ def __init__(self) -> None:
263
+ self._state = VimState.NORMAL
264
+ self._count = 0
265
+ self._operator: VimOperator | None = None
266
+ self._operator_count = 1
267
+ self._text_object_scope = ""
268
+ self._prefix = ""
269
+ self._command_buffer = ""
270
+ self._register = EditorRegister("")
271
+ self._visual_anchor: EditorLocation | None = None
272
+ self._visual_active: EditorLocation | None = None
273
+
274
+ @property
275
+ def mode(self) -> EditorMode:
276
+ return EditorMode.VIM
277
+
278
+ @property
279
+ def state(self) -> VimState:
280
+ return self._state
281
+
282
+ @property
283
+ def count(self) -> int:
284
+ return self._count
285
+
286
+ @property
287
+ def register(self) -> EditorRegister:
288
+ return self._register
289
+
290
+ @property
291
+ def command_buffer(self) -> str:
292
+ return self._command_buffer
293
+
294
+ @property
295
+ def state_label(self) -> EditorStatus:
296
+ if self._state is VimState.COMMAND:
297
+ return f"Command :{self._command_buffer}"
298
+ pending = self._pending_label()
299
+ if pending:
300
+ return f"Normal · Pending: {pending}"
301
+ labels = {
302
+ VimState.NORMAL: _VIM_MODE_NORMAL,
303
+ VimState.INSERT: _VIM_MODE_INSERT,
304
+ VimState.VISUAL_CHAR: _VIM_MODE_VISUAL,
305
+ VimState.VISUAL_LINE: _VIM_MODE_VISUAL_LINE,
306
+ VimState.OPERATOR_PENDING: _VIM_MODE_NORMAL,
307
+ VimState.TEXT_OBJECT_PENDING: _VIM_MODE_NORMAL,
308
+ VimState.REPLACE_PENDING: _VIM_MODE_NORMAL,
309
+ }
310
+ return labels[self._state].bind()
311
+
312
+ def handle_key(self, stroke: EditorKeyStroke, host: EditorCommandHost) -> EditorKeyResult:
313
+ if stroke.character is not None and len(stroke.character) == 1 and stroke.character.isprintable():
314
+ stroke = EditorKeyStroke(stroke.character, stroke.character)
315
+ if stroke.key == "ctrl+q":
316
+ return EditorKeyResult()
317
+ if stroke.key == "ctrl+insert":
318
+ return EditorKeyResult()
319
+ if stroke.key in {"tab", "shift+tab"}:
320
+ return EditorKeyResult()
321
+ if intent := COMMON_EDITOR_INTENTS.get(stroke.key):
322
+ return self._result(intent)
323
+ if self._state is VimState.INSERT:
324
+ return self._handle_insert(stroke, host)
325
+ if self._state is VimState.COMMAND:
326
+ return self._handle_command(stroke, host)
327
+ if stroke.key == "escape":
328
+ self._return_normal(host)
329
+ return self._result()
330
+ if self._state in {VimState.VISUAL_CHAR, VimState.VISUAL_LINE}:
331
+ return self._handle_visual(stroke, host)
332
+ if self._state is VimState.REPLACE_PENDING:
333
+ return self._handle_replace(stroke, host)
334
+ if self._state is VimState.TEXT_OBJECT_PENDING:
335
+ return self._handle_text_object(stroke, host)
336
+ if self._state is VimState.OPERATOR_PENDING:
337
+ return self._handle_operator_pending(stroke, host)
338
+ return self._handle_normal(stroke, host)
339
+
340
+ def reset(self) -> None:
341
+ self._state = VimState.NORMAL
342
+ self._count = 0
343
+ self._operator = None
344
+ self._operator_count = 1
345
+ self._text_object_scope = ""
346
+ self._prefix = ""
347
+ self._command_buffer = ""
348
+ self._register = EditorRegister("")
349
+ self._visual_anchor = None
350
+ self._visual_active = None
351
+
352
+ def normalize_cursor(self, host: EditorCommandHost) -> None:
353
+ """Keep the visible cursor on a Vim Normal character position."""
354
+ self._normalize_host_cursor(host)
355
+
356
+ def prepare_delegated_edit(self) -> None:
357
+ """Insert-mode edits do not invalidate Vim's modal state."""
358
+
359
+ def prepare_delegated_paste(self) -> bool:
360
+ """Permit direct document paste only while Vim is in Insert state."""
361
+ return self._state is VimState.INSERT
362
+
363
+ def _handle_normal(self, stroke: EditorKeyStroke, host: EditorCommandHost) -> EditorKeyResult:
364
+ key = stroke.key
365
+ if self._prefix:
366
+ return self._handle_prefix(key, host)
367
+ if key.isdigit() and len(key) == 1 and (key != "0" or self._count):
368
+ self._accumulate_count(int(key))
369
+ return self._result()
370
+ if key in {"g", "Z"}:
371
+ self._prefix = key
372
+ return self._result()
373
+ if operator := _OPERATORS.get(key):
374
+ self._operator = operator
375
+ self._operator_count = self._take_count()
376
+ self._state = VimState.OPERATOR_PENDING
377
+ return self._result()
378
+ if key == "r":
379
+ self._state = VimState.REPLACE_PENDING
380
+ return self._result()
381
+ if key == ":":
382
+ self._command_buffer = ""
383
+ self._state = VimState.COMMAND
384
+ self._count = 0
385
+ return self._result()
386
+ if key in _INSERT_COMMANDS:
387
+ # Counted Insert replay is deliberately excluded in v1. Discard
388
+ # the prefix uniformly so no command has partial count behavior.
389
+ self._take_count()
390
+ self._enter_insert(key, host)
391
+ return self._result()
392
+ if key in {"v", "V"}:
393
+ self._start_visual(key == "V", host)
394
+ return self._result()
395
+ if alias := _ALIASES.get(key):
396
+ count = self._take_count()
397
+ operator, motion, linewise = alias
398
+ self._apply_precomposed_operator(operator, motion, linewise, count, host)
399
+ return self._result()
400
+ if motion := _MOTIONS.get(key):
401
+ if motion is VimMotion.DOCUMENT_END and self._count:
402
+ count = self._take_count()
403
+ self._move_to_line(count - 1, host)
404
+ return self._result()
405
+ count = self._take_count()
406
+ self._move(motion, count, host)
407
+ return self._result()
408
+ if key in {"ctrl+d", "ctrl+u"}:
409
+ count = self._take_count()
410
+ self._move_half_page(key == "ctrl+d", count, host)
411
+ return self._result()
412
+ if key in {"x", "X"}:
413
+ count = self._take_count()
414
+ self._delete_characters(before=key == "X", count=count, host=host)
415
+ return self._result()
416
+ if key == "J":
417
+ self._join_lines(self._take_count(), host)
418
+ return self._result()
419
+ if key == "~":
420
+ self._toggle_case(self._take_count(), host)
421
+ return self._result()
422
+ if key in {"p", "P"}:
423
+ status = self._paste(after=key == "p", count=self._take_count(), host=host)
424
+ return self._result(status=status, warning=bool(status), character_limit=bool(status))
425
+ if key == "u":
426
+ self._count = 0
427
+ host.execute_editor_command(EditorCommand.UNDO)
428
+ self._normalize_host_cursor(host)
429
+ return self._result()
430
+ if key in {"ctrl+r", "ctrl+shift+r"}:
431
+ self._count = 0
432
+ host.execute_editor_command(EditorCommand.REDO)
433
+ self._normalize_host_cursor(host)
434
+ return self._result()
435
+ self._count = 0
436
+ return self._result()
437
+
438
+ def _handle_prefix(self, key: str, host: EditorCommandHost) -> EditorKeyResult:
439
+ prefix = self._prefix
440
+ self._prefix = ""
441
+ if prefix == "g" and key == "g":
442
+ count = self._take_count()
443
+ lines = _lines(host.editor_text)
444
+ target_row = min(count - 1, len(lines) - 1)
445
+ host.move_editor_cursor((target_row, 0))
446
+ elif prefix == "Z" and key == "Z":
447
+ self._count = 0
448
+ return self._result(EditorIntent.ACCEPT)
449
+ elif prefix == "Z" and key == "Q":
450
+ self._count = 0
451
+ return self._result(EditorIntent.CANCEL_IMMEDIATE)
452
+ else:
453
+ self._count = 0
454
+ return self._result()
455
+
456
+ def _handle_insert(self, stroke: EditorKeyStroke, host: EditorCommandHost) -> EditorKeyResult:
457
+ if stroke.key != "escape":
458
+ return EditorKeyResult()
459
+ cursor = host.editor_cursor_location
460
+ line = _lines(host.editor_text)[cursor[0]]
461
+ if cursor[1] > 0 and line:
462
+ cursor = cursor[0], cursor[1] - 1
463
+ host.move_editor_cursor(_normal_location(host.editor_text, cursor))
464
+ self._state = VimState.NORMAL
465
+ return self._result()
466
+
467
+ def _handle_replace(self, stroke: EditorKeyStroke, host: EditorCommandHost) -> EditorKeyResult:
468
+ character = stroke.character
469
+ if character is None or len(character) != 1 or character in {"\n", "\r"}:
470
+ self._return_normal(host)
471
+ return self._result()
472
+ text = host.editor_text
473
+ row, column = _normal_location(text, host.editor_cursor_location)
474
+ line = _lines(text)[row]
475
+ count = min(self._take_count(), max(len(line) - column, 0))
476
+ if count:
477
+ source_range = EditorTextRange((row, column), (row, column + count))
478
+ host.replace_editor_range(source_range, character * count)
479
+ host.move_editor_cursor((row, column + count - 1))
480
+ else:
481
+ host.notify_editor_activity()
482
+ self._state = VimState.NORMAL
483
+ return self._result()
484
+
485
+ def _handle_operator_pending(self, stroke: EditorKeyStroke, host: EditorCommandHost) -> EditorKeyResult:
486
+ key = stroke.key
487
+ if self._prefix:
488
+ prefix = self._prefix
489
+ self._prefix = ""
490
+ operator = self._operator
491
+ if prefix == "g" and key == "g" and operator is not None:
492
+ explicit_count = self._count > 0 or self._operator_count > 1
493
+ if explicit_count:
494
+ self._apply_operator_to_line(operator, self._effective_operator_count() - 1, host)
495
+ else:
496
+ self._apply_operator_to_document_edge(operator, start=True, host=host)
497
+ else:
498
+ self._return_normal(host)
499
+ return self._result()
500
+ if key.isdigit() and len(key) == 1 and (key != "0" or self._count):
501
+ self._accumulate_count(int(key))
502
+ return self._result()
503
+ operator = self._operator
504
+ if operator is None:
505
+ self._return_normal(host)
506
+ return self._result()
507
+ if key in {"i", "a"}:
508
+ self._text_object_scope = key
509
+ self._state = VimState.TEXT_OBJECT_PENDING
510
+ return self._result()
511
+ if key == operator.value:
512
+ effective = self._effective_operator_count()
513
+ self._apply_line_operator(operator, effective, host)
514
+ return self._result()
515
+ if key == "g":
516
+ self._prefix = "g"
517
+ return self._result()
518
+ if motion := _MOTIONS.get(key):
519
+ if motion not in _OPERATOR_MOTIONS:
520
+ self._return_normal(host)
521
+ return self._result()
522
+ explicit_document_line = motion is VimMotion.DOCUMENT_END and (self._count > 0 or self._operator_count > 1)
523
+ count = self._effective_operator_count()
524
+ if explicit_document_line:
525
+ self._apply_operator_to_line(operator, count - 1, host)
526
+ else:
527
+ self._apply_motion_operator(operator, motion, count, host)
528
+ return self._result()
529
+ self._return_normal(host)
530
+ return self._result()
531
+
532
+ def _handle_text_object(self, stroke: EditorKeyStroke, host: EditorCommandHost) -> EditorKeyResult:
533
+ operator = self._operator
534
+ text_object = _TEXT_OBJECTS.get(stroke.key)
535
+ if operator is None or text_object is None:
536
+ self._return_normal(host)
537
+ return self._result()
538
+ source_range = self._resolve_text_object(
539
+ text_object,
540
+ around=self._text_object_scope == "a",
541
+ count=self._effective_operator_count(),
542
+ host=host,
543
+ )
544
+ if source_range is None:
545
+ self._return_normal(host)
546
+ return self._result()
547
+ self._apply_operator(operator, source_range, linewise=False, host=host)
548
+ return self._result()
549
+
550
+ def _handle_visual(self, stroke: EditorKeyStroke, host: EditorCommandHost) -> EditorKeyResult:
551
+ key = stroke.key
552
+ if self._prefix:
553
+ prefix = self._prefix
554
+ self._prefix = ""
555
+ if prefix == "g" and key == "g":
556
+ self._move_to_visual_line(self._take_count() - 1, host)
557
+ return self._result()
558
+ if key.isdigit() and len(key) == 1 and (key != "0" or self._count):
559
+ self._accumulate_count(int(key))
560
+ return self._result()
561
+ if motion := _MOTIONS.get(key):
562
+ if motion is VimMotion.DOCUMENT_END and self._count:
563
+ self._move_to_visual_line(self._take_count() - 1, host)
564
+ return self._result()
565
+ self._move_visual(motion, self._take_count(), host)
566
+ return self._result()
567
+ if key == "g":
568
+ self._prefix = "g"
569
+ return self._result()
570
+ if key in {"ctrl+d", "ctrl+u"}:
571
+ count = self._take_count()
572
+ active = self._visual_active or _normal_location(host.editor_text, host.editor_cursor_location)
573
+ distance = max(host.editor_page_height // 2, 1) * count
574
+ row = active[0] + (distance if key == "ctrl+d" else -distance)
575
+ self._move_to_visual_line(row, host)
576
+ return self._result()
577
+ if key == "o":
578
+ self._visual_anchor, self._visual_active = self._visual_active, self._visual_anchor
579
+ self._render_visual_selection(host)
580
+ return self._result()
581
+ if key in {"y", "d", "x", "c"}:
582
+ operator = VimOperator.YANK if key == "y" else VimOperator.CHANGE if key == "c" else VimOperator.DELETE
583
+ source_range = self._visual_range(host)
584
+ linewise = self._state is VimState.VISUAL_LINE
585
+ if linewise:
586
+ anchor = self._visual_anchor or host.editor_cursor_location
587
+ active = self._visual_active or host.editor_cursor_location
588
+ start_row, end_row = sorted((anchor[0], active[0]))
589
+ if operator is VimOperator.CHANGE:
590
+ source_range = self._line_change_range(host.editor_text, start_row, end_row - start_row + 1)
591
+ self._apply_operator(
592
+ operator,
593
+ source_range,
594
+ linewise=linewise,
595
+ linewise_start_row=start_row if linewise else None,
596
+ host=host,
597
+ )
598
+ if linewise and operator is not VimOperator.YANK:
599
+ self._position_after_line_operator(start_row, host)
600
+ return self._result()
601
+ self._count = 0
602
+ return self._result()
603
+
604
+ def _handle_command(self, stroke: EditorKeyStroke, host: EditorCommandHost) -> EditorKeyResult:
605
+ if stroke.key == "escape":
606
+ self._command_buffer = ""
607
+ self._state = VimState.NORMAL
608
+ return self._result()
609
+ if stroke.key == "backspace":
610
+ self._command_buffer = self._command_buffer[:-1]
611
+ return self._result()
612
+ if stroke.key == "enter":
613
+ command = self._command_buffer
614
+ if command in {"wq", "x"}:
615
+ return self._result(EditorIntent.ACCEPT)
616
+ if command == "q!":
617
+ return self._result(EditorIntent.CANCEL_IMMEDIATE)
618
+ if command == "q":
619
+ if not host.editor_is_dirty:
620
+ return self._result(EditorIntent.CANCEL_IMMEDIATE)
621
+ return self._result(status=_VIM_NO_WRITE.bind(), warning=True)
622
+ return self._result(status=_VIM_NOT_EDITOR_COMMAND.bind(command=command), warning=True)
623
+ if stroke.character is not None and stroke.character.isprintable():
624
+ self._command_buffer += stroke.character
625
+ return self._result()
626
+
627
+ def _start_visual(self, linewise: bool, host: EditorCommandHost) -> None:
628
+ location = _normal_location(host.editor_text, host.editor_cursor_location)
629
+ self._visual_anchor = location
630
+ self._visual_active = location
631
+ self._state = VimState.VISUAL_LINE if linewise else VimState.VISUAL_CHAR
632
+ self._count = 0
633
+ self._render_visual_selection(host)
634
+
635
+ def _move_visual(self, motion: VimMotion, count: int, host: EditorCommandHost) -> None:
636
+ active = self._visual_active or _normal_location(host.editor_text, host.editor_cursor_location)
637
+ target = self._resolve_motion_target(motion, count, active, host.editor_text).location
638
+ self._visual_active = _normal_location(host.editor_text, target)
639
+ self._render_visual_selection(host)
640
+
641
+ def _move_to_visual_line(self, row: int, host: EditorCommandHost) -> None:
642
+ active = self._visual_active or _normal_location(host.editor_text, host.editor_cursor_location)
643
+ lines = _lines(host.editor_text)
644
+ target_row = min(max(row, 0), len(lines) - 1)
645
+ self._visual_active = _normal_location(host.editor_text, (target_row, active[1]))
646
+ self._render_visual_selection(host)
647
+
648
+ def _render_visual_selection(self, host: EditorCommandHost) -> None:
649
+ anchor = self._visual_anchor
650
+ active = self._visual_active
651
+ if anchor is None or active is None:
652
+ return
653
+ if self._state is VimState.VISUAL_LINE:
654
+ start_row, end_row = sorted((anchor[0], active[0]))
655
+ source_range = self._line_edit_range(host.editor_text, start_row, end_row - start_row + 1)
656
+ if active[0] >= anchor[0]:
657
+ host.set_editor_selection(source_range.start, source_range.end)
658
+ else:
659
+ host.set_editor_selection(source_range.end, source_range.start)
660
+ return
661
+ anchor_offset = location_to_offset(host.editor_text, anchor)
662
+ active_offset = location_to_offset(host.editor_text, active)
663
+ if anchor_offset <= active_offset:
664
+ host.set_editor_selection(anchor, _advance_location(host.editor_text, active))
665
+ else:
666
+ host.set_editor_selection(_advance_location(host.editor_text, anchor), active)
667
+
668
+ def _visual_range(self, host: EditorCommandHost) -> EditorTextRange:
669
+ text = host.editor_text
670
+ anchor = self._visual_anchor or host.editor_cursor_location
671
+ active = self._visual_active or host.editor_cursor_location
672
+ if self._state is VimState.VISUAL_LINE:
673
+ start_row, end_row = sorted((anchor[0], active[0]))
674
+ return self._line_edit_range(text, start_row, end_row - start_row + 1)
675
+ start = min(location_to_offset(text, anchor), location_to_offset(text, active))
676
+ end = min(max(location_to_offset(text, anchor), location_to_offset(text, active)) + 1, len(text))
677
+ return _range_from_offsets(text, start, end)
678
+
679
+ def _enter_insert(self, command: str, host: EditorCommandHost) -> None:
680
+ text = host.editor_text
681
+ lines = _lines(text)
682
+ row, column = _normal_location(text, host.editor_cursor_location)
683
+ line = lines[row]
684
+ if command == "i":
685
+ location = row, column
686
+ elif command == "I":
687
+ location = row, _first_non_blank(line)
688
+ elif command == "a":
689
+ location = row, min(column + 1, len(line))
690
+ elif command == "A":
691
+ location = row, len(line)
692
+ elif command == "o":
693
+ insert_at = (row, len(line))
694
+ host.replace_editor_range(EditorTextRange(insert_at, insert_at), "\n")
695
+ location = row + 1, 0
696
+ else:
697
+ insert_at = (row, 0)
698
+ host.replace_editor_range(EditorTextRange(insert_at, insert_at), "\n")
699
+ location = row, 0
700
+ host.move_editor_cursor(location)
701
+ self._state = VimState.INSERT
702
+
703
+ def _move(self, motion: VimMotion, count: int, host: EditorCommandHost) -> None:
704
+ current = _normal_location(host.editor_text, host.editor_cursor_location)
705
+ target = self._resolve_motion_target(motion, count, current, host.editor_text)
706
+ host.move_editor_cursor(_normal_location(host.editor_text, target.location))
707
+
708
+ def _move_to_line(self, row: int, host: EditorCommandHost) -> None:
709
+ lines = _lines(host.editor_text)
710
+ target_row = min(max(row, 0), len(lines) - 1)
711
+ host.move_editor_cursor(_normal_location(host.editor_text, (target_row, 0)))
712
+
713
+ def _move_half_page(self, down: bool, count: int, host: EditorCommandHost) -> None:
714
+ text = host.editor_text
715
+ lines = _lines(text)
716
+ row, column = _normal_location(text, host.editor_cursor_location)
717
+ distance = max(host.editor_page_height // 2, 1) * count
718
+ row = min(row + distance, len(lines) - 1) if down else max(row - distance, 0)
719
+ host.move_editor_cursor(_normal_location(text, (row, column)))
720
+
721
+ def _resolve_motion_target(
722
+ self,
723
+ motion: VimMotion,
724
+ count: int,
725
+ current: EditorLocation,
726
+ text: str,
727
+ ) -> _MotionTarget:
728
+ lines = _lines(text)
729
+ row, column = _normal_location(text, current)
730
+ if motion is VimMotion.LEFT:
731
+ return _MotionTarget((row, max(column - count, 0)))
732
+ if motion is VimMotion.RIGHT:
733
+ return _MotionTarget((row, min(column + count, max(len(lines[row]) - 1, 0))))
734
+ if motion in {VimMotion.DOWN, VimMotion.UP}:
735
+ row_delta = count if motion is VimMotion.DOWN else -count
736
+ target_row = min(max(row + row_delta, 0), len(lines) - 1)
737
+ return _MotionTarget(_normal_location(text, (target_row, column)))
738
+ if motion is VimMotion.LINE_START:
739
+ return _MotionTarget((row, 0))
740
+ if motion is VimMotion.FIRST_NON_BLANK:
741
+ return _MotionTarget((row, _first_non_blank(lines[row])))
742
+ if motion is VimMotion.LINE_END:
743
+ target_row = min(row + count - 1, len(lines) - 1)
744
+ target_line = lines[target_row]
745
+ return _MotionTarget((target_row, max(len(target_line) - 1, 0)), inclusive=bool(target_line))
746
+ if motion is VimMotion.WORD_NEXT:
747
+ offset = _next_word_start(text, location_to_offset(text, (row, column)), count)
748
+ return _MotionTarget(offset_to_location(text, offset))
749
+ if motion is VimMotion.WORD_PREVIOUS:
750
+ offset = _previous_word_start(text, location_to_offset(text, (row, column)), count)
751
+ return _MotionTarget(offset_to_location(text, offset))
752
+ if motion is VimMotion.WORD_END:
753
+ offset = _word_end(text, location_to_offset(text, (row, column)), count)
754
+ return _MotionTarget(offset_to_location(text, offset), inclusive=True)
755
+ if motion is VimMotion.DOCUMENT_START:
756
+ return _MotionTarget((0, 0), linewise=True)
757
+ return _MotionTarget((len(lines) - 1, max(len(lines[-1]) - 1, 0)), inclusive=True, linewise=True)
758
+
759
+ def _apply_precomposed_operator(
760
+ self,
761
+ operator: VimOperator,
762
+ motion: VimMotion | None,
763
+ linewise: bool,
764
+ count: int,
765
+ host: EditorCommandHost,
766
+ ) -> None:
767
+ if linewise:
768
+ self._apply_line_operator(operator, count, host)
769
+ elif motion is not None:
770
+ self._apply_motion_operator(operator, motion, count, host)
771
+
772
+ def _apply_motion_operator(
773
+ self,
774
+ operator: VimOperator,
775
+ motion: VimMotion,
776
+ count: int,
777
+ host: EditorCommandHost,
778
+ ) -> None:
779
+ text = host.editor_text
780
+ current = _normal_location(text, host.editor_cursor_location)
781
+ if motion is VimMotion.RIGHT:
782
+ row, column = current
783
+ end = min(column + count, len(_lines(text)[row]))
784
+ self._apply_operator(operator, EditorTextRange(current, (row, end)), linewise=False, host=host)
785
+ return
786
+ if motion is VimMotion.LEFT:
787
+ row, column = current
788
+ start = max(column - count, 0)
789
+ self._apply_operator(operator, EditorTextRange((row, start), current), linewise=False, host=host)
790
+ return
791
+ if operator is VimOperator.CHANGE and motion is VimMotion.WORD_NEXT and count == 1:
792
+ current_offset = location_to_offset(text, current)
793
+ if current_offset < len(text) and not text[current_offset].isspace():
794
+ target = self._resolve_motion_target(VimMotion.WORD_END, 1, current, text)
795
+ end_offset = min(location_to_offset(text, target.location) + 1, len(text))
796
+ self._apply_operator(
797
+ operator,
798
+ _range_from_offsets(text, current_offset, end_offset),
799
+ linewise=False,
800
+ host=host,
801
+ )
802
+ return
803
+ target = self._resolve_motion_target(motion, count, current, text)
804
+ if target.linewise:
805
+ start_row, end_row = sorted((current[0], target.location[0]))
806
+ range_factory = self._line_change_range if operator is VimOperator.CHANGE else self._line_edit_range
807
+ source_range = range_factory(text, start_row, end_row - start_row + 1)
808
+ self._apply_operator(
809
+ operator,
810
+ source_range,
811
+ linewise=True,
812
+ linewise_start_row=start_row,
813
+ host=host,
814
+ )
815
+ if operator is not VimOperator.YANK:
816
+ self._position_after_line_operator(start_row, host)
817
+ return
818
+ start_offset = location_to_offset(text, current)
819
+ end_offset = location_to_offset(text, target.location)
820
+ if target.inclusive:
821
+ if end_offset >= start_offset:
822
+ end_offset = min(end_offset + 1, len(text))
823
+ else:
824
+ start_offset = min(start_offset + 1, len(text))
825
+ source_range = _range_from_offsets(text, start_offset, end_offset)
826
+ self._apply_operator(operator, source_range, linewise=False, host=host)
827
+
828
+ def _apply_operator_to_document_edge(
829
+ self,
830
+ operator: VimOperator,
831
+ *,
832
+ start: bool,
833
+ host: EditorCommandHost,
834
+ ) -> None:
835
+ row = _normal_location(host.editor_text, host.editor_cursor_location)[0]
836
+ if start:
837
+ start_row = 0
838
+ count = row + 1
839
+ else:
840
+ start_row = row
841
+ count = len(_lines(host.editor_text)) - row
842
+ range_factory = self._line_change_range if operator is VimOperator.CHANGE else self._line_edit_range
843
+ source_range = range_factory(host.editor_text, start_row, count)
844
+ self._apply_operator(
845
+ operator,
846
+ source_range,
847
+ linewise=True,
848
+ linewise_start_row=start_row,
849
+ host=host,
850
+ )
851
+ if operator is not VimOperator.YANK:
852
+ self._position_after_line_operator(start_row, host)
853
+
854
+ def _apply_operator_to_line(self, operator: VimOperator, row: int, host: EditorCommandHost) -> None:
855
+ current_row = _normal_location(host.editor_text, host.editor_cursor_location)[0]
856
+ lines = _lines(host.editor_text)
857
+ target_row = min(max(row, 0), len(lines) - 1)
858
+ start_row, end_row = sorted((current_row, target_row))
859
+ count = end_row - start_row + 1
860
+ range_factory = self._line_change_range if operator is VimOperator.CHANGE else self._line_edit_range
861
+ self._apply_operator(
862
+ operator,
863
+ range_factory(host.editor_text, start_row, count),
864
+ linewise=True,
865
+ linewise_start_row=start_row,
866
+ host=host,
867
+ )
868
+ if operator is not VimOperator.YANK:
869
+ self._position_after_line_operator(start_row, host)
870
+
871
+ def _apply_line_operator(self, operator: VimOperator, count: int, host: EditorCommandHost) -> None:
872
+ row = _normal_location(host.editor_text, host.editor_cursor_location)[0]
873
+ range_factory = self._line_change_range if operator is VimOperator.CHANGE else self._line_edit_range
874
+ source_range = range_factory(host.editor_text, row, count)
875
+ self._apply_operator(
876
+ operator,
877
+ source_range,
878
+ linewise=True,
879
+ linewise_start_row=row,
880
+ host=host,
881
+ )
882
+ if operator is not VimOperator.YANK:
883
+ self._position_after_line_operator(row, host)
884
+
885
+ def _apply_operator(
886
+ self,
887
+ operator: VimOperator,
888
+ source_range: EditorTextRange,
889
+ *,
890
+ linewise: bool,
891
+ linewise_start_row: int | None = None,
892
+ host: EditorCommandHost,
893
+ ) -> None:
894
+ text = host.read_editor_range(source_range)
895
+ if linewise:
896
+ register_text = self._linewise_register_text(text, source_range, host.editor_text)
897
+ if register_text:
898
+ self._register = EditorRegister(register_text, EditorRegisterKind.LINEWISE)
899
+ else:
900
+ if text:
901
+ self._register = EditorRegister(text)
902
+ target_state = VimState.INSERT if operator is VimOperator.CHANGE else VimState.NORMAL
903
+ if operator is VimOperator.YANK:
904
+ cursor = (linewise_start_row, 0) if linewise_start_row is not None else source_range.start
905
+ host.move_editor_cursor(_normal_location(host.editor_text, cursor))
906
+ elif text:
907
+ host.delete_editor_range(source_range)
908
+ if target_state is VimState.NORMAL:
909
+ self._normalize_host_cursor(host)
910
+ else:
911
+ host.notify_editor_activity()
912
+ self._state = target_state
913
+ self._clear_pending()
914
+
915
+ def _delete_characters(self, *, before: bool, count: int, host: EditorCommandHost) -> None:
916
+ text = host.editor_text
917
+ row, column = _normal_location(text, host.editor_cursor_location)
918
+ line = _lines(text)[row]
919
+ start = max(column - count, 0) if before else column
920
+ end = column if before else min(column + count, len(line))
921
+ source_range = EditorTextRange((row, start), (row, end))
922
+ deleted = host.read_editor_range(source_range)
923
+ if deleted:
924
+ self._register = EditorRegister(deleted)
925
+ host.delete_editor_range(source_range)
926
+ self._normalize_host_cursor(host)
927
+ else:
928
+ host.notify_editor_activity()
929
+
930
+ def _join_lines(self, count: int, host: EditorCommandHost) -> None:
931
+ text = host.editor_text
932
+ lines = _lines(text)
933
+ row = _normal_location(text, host.editor_cursor_location)[0]
934
+ end_row = min(row + max(count, 2) - 1, len(lines) - 1)
935
+ if end_row == row:
936
+ host.notify_editor_activity()
937
+ return
938
+ replacement = lines[row].rstrip()
939
+ for line in lines[row + 1 : end_row + 1]:
940
+ replacement += " " + line.strip()
941
+ source_range = EditorTextRange((row, 0), (end_row, len(lines[end_row])))
942
+ cursor_column = len(lines[row].rstrip())
943
+ host.replace_editor_range(source_range, replacement)
944
+ host.move_editor_cursor((row, min(cursor_column, max(len(replacement) - 1, 0))))
945
+
946
+ def _toggle_case(self, count: int, host: EditorCommandHost) -> None:
947
+ text = host.editor_text
948
+ row, column = _normal_location(text, host.editor_cursor_location)
949
+ line = _lines(text)[row]
950
+ end = min(column + count, len(line))
951
+ source_range = EditorTextRange((row, column), (row, end))
952
+ current = host.read_editor_range(source_range)
953
+ if not current:
954
+ host.notify_editor_activity()
955
+ return
956
+ replacement = "".join(character.lower() if character.isupper() else character.upper() for character in current)
957
+ host.replace_editor_range(source_range, replacement)
958
+ host.move_editor_cursor(_normal_location(host.editor_text, (row, end)))
959
+
960
+ def _paste(self, *, after: bool, count: int, host: EditorCommandHost) -> EditorStatus:
961
+ register = self._register
962
+ if not register.text:
963
+ host.notify_editor_activity()
964
+ return ""
965
+ text = host.editor_text
966
+ inserted_character_count = len(register.text) * count
967
+ if inserted_character_count > MESSAGE_EDITOR_MAX_CHARACTERS - len(text):
968
+ host.notify_editor_activity()
969
+ return MESSAGE_EDITOR_CHARACTER_LIMIT_STATUS.bind()
970
+ row, column = _normal_location(text, host.editor_cursor_location)
971
+ payload = register.text * count
972
+ if register.kind is EditorRegisterKind.LINEWISE:
973
+ lines = _lines(text)
974
+ if after:
975
+ if row < len(lines) - 1:
976
+ location = row + 1, 0
977
+ inserted = payload
978
+ else:
979
+ location = row, len(lines[row])
980
+ inserted = "\n" + payload.removesuffix("\n")
981
+ else:
982
+ location = row, 0
983
+ inserted = payload
984
+ host.replace_editor_range(EditorTextRange(location, location), inserted)
985
+ target_row = row + 1 if after else row
986
+ host.move_editor_cursor((min(target_row, len(_lines(host.editor_text)) - 1), 0))
987
+ return ""
988
+ location = row, min(column + 1, len(_lines(text)[row])) if after else column
989
+ insertion_offset = location_to_offset(text, location)
990
+ host.replace_editor_range(EditorTextRange(location, location), payload)
991
+ target = offset_to_location(host.editor_text, insertion_offset + len(payload) - 1)
992
+ host.move_editor_cursor(_normal_location(host.editor_text, target))
993
+ return ""
994
+
995
+ def _resolve_text_object(
996
+ self,
997
+ text_object: VimTextObject,
998
+ *,
999
+ around: bool,
1000
+ count: int,
1001
+ host: EditorCommandHost,
1002
+ ) -> EditorTextRange | None:
1003
+ text = host.editor_text
1004
+ cursor_offset = location_to_offset(text, _normal_location(text, host.editor_cursor_location))
1005
+ if text_object in {VimTextObject.WORD, VimTextObject.WORD_BIG}:
1006
+ return self._word_object(
1007
+ text,
1008
+ cursor_offset,
1009
+ around=around,
1010
+ big=text_object is VimTextObject.WORD_BIG,
1011
+ count=count,
1012
+ )
1013
+ if text_object in {VimTextObject.DOUBLE_QUOTE, VimTextObject.SINGLE_QUOTE, VimTextObject.BACKTICK}:
1014
+ return self._quote_object(text, cursor_offset, text_object.value, around=around)
1015
+ delimiters = {
1016
+ VimTextObject.PAREN: ("(", ")"),
1017
+ VimTextObject.BRACKET: ("[", "]"),
1018
+ VimTextObject.BRACE: ("{", "}"),
1019
+ }
1020
+ opening, closing = delimiters[text_object]
1021
+ return self._pair_object(text, cursor_offset, opening, closing, around=around, count=count)
1022
+
1023
+ def _word_object(
1024
+ self,
1025
+ text: str,
1026
+ cursor: int,
1027
+ *,
1028
+ around: bool,
1029
+ big: bool,
1030
+ count: int,
1031
+ ) -> EditorTextRange | None:
1032
+ if not text:
1033
+ return None
1034
+ index = min(cursor, len(text) - 1)
1035
+ if text[index].isspace():
1036
+ while index < len(text) and text[index].isspace():
1037
+ index += 1
1038
+ if index == len(text):
1039
+ return None
1040
+ character_class = _token_class(text[index], big=big)
1041
+ start = index
1042
+ end = index + 1
1043
+ while start > 0 and _token_class(text[start - 1], big=big) == character_class:
1044
+ start -= 1
1045
+ while end < len(text) and _token_class(text[end], big=big) == character_class:
1046
+ end += 1
1047
+ for _ in range(count - 1):
1048
+ while end < len(text) and text[end].isspace():
1049
+ end += 1
1050
+ if end >= len(text):
1051
+ break
1052
+ character_class = _token_class(text[end], big=big)
1053
+ while end < len(text) and _token_class(text[end], big=big) == character_class:
1054
+ end += 1
1055
+ if around:
1056
+ if end < len(text) and text[end].isspace():
1057
+ while end < len(text) and text[end].isspace():
1058
+ end += 1
1059
+ else:
1060
+ while start > 0 and text[start - 1].isspace():
1061
+ start -= 1
1062
+ return _range_from_offsets(text, start, end)
1063
+
1064
+ def _quote_object(self, text: str, cursor: int, quote: str, *, around: bool) -> EditorTextRange | None:
1065
+ line_start = text.rfind("\n", 0, cursor) + 1
1066
+ line_end = text.find("\n", cursor)
1067
+ if line_end == -1:
1068
+ line_end = len(text)
1069
+ delimiters = [index for index in range(line_start, line_end) if text[index] == quote]
1070
+ pair = next(
1071
+ (
1072
+ (delimiters[index], delimiters[index + 1])
1073
+ for index in range(0, len(delimiters) - 1, 2)
1074
+ if delimiters[index] <= cursor <= delimiters[index + 1]
1075
+ ),
1076
+ None,
1077
+ )
1078
+ if pair is None:
1079
+ return None
1080
+ opening, closing = pair
1081
+ start = opening if around else opening + 1
1082
+ end = closing + 1 if around else closing
1083
+ return _range_from_offsets(text, start, end)
1084
+
1085
+ def _pair_object(
1086
+ self,
1087
+ text: str,
1088
+ cursor: int,
1089
+ opening: str,
1090
+ closing: str,
1091
+ *,
1092
+ around: bool,
1093
+ count: int,
1094
+ ) -> EditorTextRange | None:
1095
+ stack: list[int] = []
1096
+ candidates: list[tuple[int, int]] = []
1097
+ for index, character in enumerate(text):
1098
+ if character == opening:
1099
+ stack.append(index)
1100
+ elif character == closing and stack:
1101
+ start = stack.pop()
1102
+ if start <= cursor <= index:
1103
+ candidates.append((start, index))
1104
+ if not candidates:
1105
+ return None
1106
+ candidates.sort(key=lambda pair: pair[1] - pair[0])
1107
+ selected = candidates[min(count - 1, len(candidates) - 1)]
1108
+ start = selected[0] if around else selected[0] + 1
1109
+ end = selected[1] + 1 if around else selected[1]
1110
+ return _range_from_offsets(text, start, end)
1111
+
1112
+ def _line_edit_range(self, text: str, start_row: int, count: int) -> EditorTextRange:
1113
+ lines = _lines(text)
1114
+ start_row = min(max(start_row, 0), len(lines) - 1)
1115
+ end_row = min(start_row + max(count, 1) - 1, len(lines) - 1)
1116
+ if end_row < len(lines) - 1:
1117
+ return EditorTextRange((start_row, 0), (end_row + 1, 0))
1118
+ if start_row > 0:
1119
+ return EditorTextRange((start_row - 1, len(lines[start_row - 1])), (end_row, len(lines[end_row])))
1120
+ return EditorTextRange((0, 0), (end_row, len(lines[end_row])))
1121
+
1122
+ def _line_change_range(self, text: str, start_row: int, count: int) -> EditorTextRange:
1123
+ lines = _lines(text)
1124
+ start_row = min(max(start_row, 0), len(lines) - 1)
1125
+ end_row = min(start_row + max(count, 1) - 1, len(lines) - 1)
1126
+ return EditorTextRange((start_row, 0), (end_row, len(lines[end_row])))
1127
+
1128
+ def _linewise_register_text(
1129
+ self,
1130
+ selected_text: str,
1131
+ source_range: EditorTextRange,
1132
+ document_text: str,
1133
+ ) -> str:
1134
+ lines = _lines(document_text)
1135
+ if source_range.end[0] > source_range.start[0] and source_range.end[1] == 0:
1136
+ return selected_text
1137
+ starts_with_preceding_newline = source_range.end[0] > source_range.start[0] and source_range.start[1] == len(
1138
+ lines[source_range.start[0]]
1139
+ )
1140
+ if starts_with_preceding_newline and selected_text.startswith("\n"):
1141
+ selected_text = selected_text[1:]
1142
+ return selected_text + "\n"
1143
+
1144
+ def _normalize_host_cursor(self, host: EditorCommandHost) -> None:
1145
+ host.move_editor_cursor(_normal_location(host.editor_text, host.editor_cursor_location))
1146
+
1147
+ def _position_after_line_operator(self, start_row: int, host: EditorCommandHost) -> None:
1148
+ lines = _lines(host.editor_text)
1149
+ host.move_editor_cursor((min(start_row, len(lines) - 1), 0))
1150
+
1151
+ def _return_normal(self, host: EditorCommandHost) -> None:
1152
+ visual_active = self._visual_active if self._state in {VimState.VISUAL_CHAR, VimState.VISUAL_LINE} else None
1153
+ self._state = VimState.NORMAL
1154
+ self._clear_pending()
1155
+ self._visual_anchor = None
1156
+ self._visual_active = None
1157
+ target = visual_active or host.editor_cursor_location
1158
+ host.move_editor_cursor(_normal_location(host.editor_text, target))
1159
+
1160
+ def _clear_pending(self) -> None:
1161
+ self._count = 0
1162
+ self._operator = None
1163
+ self._operator_count = 1
1164
+ self._text_object_scope = ""
1165
+ self._prefix = ""
1166
+
1167
+ def _accumulate_count(self, digit: int) -> None:
1168
+ self._count = min(self._count * 10 + digit, MAX_VIM_COUNT)
1169
+
1170
+ def _take_count(self) -> int:
1171
+ count = self._count or 1
1172
+ self._count = 0
1173
+ return count
1174
+
1175
+ def _effective_operator_count(self) -> int:
1176
+ return min(self._operator_count * self._take_count(), MAX_VIM_COUNT)
1177
+
1178
+ def _pending_label(self) -> str:
1179
+ if self._state is VimState.REPLACE_PENDING:
1180
+ return f"{self._count or ''}r"
1181
+ count = str(self._count) if self._count else ""
1182
+ operator = self._operator.value if self._operator is not None else ""
1183
+ return f"{self._operator_count if self._operator_count > 1 else ''}{operator}{count}{self._text_object_scope}{self._prefix}"
1184
+
1185
+ def _result(
1186
+ self,
1187
+ intent: EditorIntent = EditorIntent.NONE,
1188
+ status: EditorStatus = "",
1189
+ *,
1190
+ warning: bool = False,
1191
+ character_limit: bool = False,
1192
+ ) -> EditorKeyResult:
1193
+ return EditorKeyResult(
1194
+ consumed=True, intent=intent, status=status, warning=warning, character_limit=character_limit
1195
+ )