connectonion 0.6.1__py3-none-any.whl → 0.6.3__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 (413) hide show
  1. connectonion/__init__.py +46 -9
  2. connectonion/cli/__init__.py +11 -1
  3. connectonion/cli/browser_agent/__init__.py +11 -1
  4. connectonion/cli/browser_agent/browser.py +95 -142
  5. connectonion/cli/browser_agent/element_finder.py +147 -0
  6. connectonion/cli/browser_agent/highlight_screenshot.py +182 -0
  7. connectonion/cli/browser_agent/prompt.md +188 -105
  8. connectonion/cli/browser_agent/prompts/element_matcher.md +59 -0
  9. connectonion/cli/browser_agent/prompts/form_filler.md +19 -0
  10. connectonion/cli/browser_agent/prompts/scroll_strategy.md +36 -0
  11. connectonion/cli/browser_agent/scripts/extract_elements.js +126 -0
  12. connectonion/cli/browser_agent/scroll.py +145 -0
  13. connectonion/cli/co_ai/__init__.py +6 -0
  14. connectonion/cli/co_ai/agent.py +87 -0
  15. connectonion/cli/co_ai/agents/__init__.py +5 -0
  16. connectonion/cli/co_ai/agents/registry.py +57 -0
  17. connectonion/cli/co_ai/commands/__init__.py +45 -0
  18. connectonion/cli/co_ai/commands/compact.py +173 -0
  19. connectonion/cli/co_ai/commands/cost.py +77 -0
  20. connectonion/cli/co_ai/commands/export.py +60 -0
  21. connectonion/cli/co_ai/commands/help.py +80 -0
  22. connectonion/cli/co_ai/commands/init.py +101 -0
  23. connectonion/cli/co_ai/commands/sessions.py +55 -0
  24. connectonion/cli/co_ai/commands/tasks.py +63 -0
  25. connectonion/cli/co_ai/commands/undo.py +103 -0
  26. connectonion/cli/co_ai/context.py +127 -0
  27. connectonion/cli/co_ai/main.py +52 -0
  28. connectonion/cli/co_ai/plugins/__init__.py +6 -0
  29. connectonion/cli/co_ai/plugins/reminder.py +76 -0
  30. connectonion/cli/co_ai/plugins/shell_approval.py +105 -0
  31. connectonion/cli/co_ai/prompts/agents/explore.md +79 -0
  32. connectonion/cli/co_ai/prompts/agents/plan.md +60 -0
  33. connectonion/cli/co_ai/prompts/assembler.py +303 -0
  34. connectonion/cli/{docs/co-vibecoding-principles-docs-contexts-all-in-one.md → co_ai/prompts/connectonion/README.md} +26 -0
  35. connectonion/cli/co_ai/prompts/connectonion/api.md +457 -0
  36. connectonion/cli/co_ai/prompts/connectonion/cli/README.md +805 -0
  37. connectonion/cli/co_ai/prompts/connectonion/cli/auth.md +46 -0
  38. connectonion/cli/co_ai/prompts/connectonion/cli/browser.md +235 -0
  39. connectonion/cli/co_ai/prompts/connectonion/cli/copy.md +184 -0
  40. connectonion/cli/co_ai/prompts/connectonion/cli/create.md +335 -0
  41. connectonion/cli/co_ai/prompts/connectonion/cli/init.md +431 -0
  42. connectonion/cli/co_ai/prompts/connectonion/co-directory-structure.md +214 -0
  43. connectonion/cli/co_ai/prompts/connectonion/concepts/agent.md +1078 -0
  44. connectonion/cli/co_ai/prompts/connectonion/concepts/events.md +816 -0
  45. connectonion/cli/co_ai/prompts/connectonion/concepts/llm_do.md +256 -0
  46. connectonion/cli/co_ai/prompts/connectonion/concepts/max_iterations.md +362 -0
  47. connectonion/cli/co_ai/prompts/connectonion/concepts/models.md +641 -0
  48. connectonion/cli/co_ai/prompts/connectonion/concepts/plugins.md +100 -0
  49. connectonion/cli/co_ai/prompts/connectonion/concepts/prompts.md +122 -0
  50. connectonion/cli/co_ai/prompts/connectonion/concepts/tools.md +512 -0
  51. connectonion/cli/co_ai/prompts/connectonion/concepts/transcribe.md +156 -0
  52. connectonion/cli/co_ai/prompts/connectonion/concepts/trust.md +291 -0
  53. connectonion/cli/co_ai/prompts/connectonion/debug/README.md +18 -0
  54. connectonion/cli/co_ai/prompts/connectonion/debug/auto_debug.md +1026 -0
  55. connectonion/cli/co_ai/prompts/connectonion/debug/console.md +129 -0
  56. connectonion/cli/co_ai/prompts/connectonion/debug/eval-format.md +178 -0
  57. connectonion/cli/co_ai/prompts/connectonion/debug/eval.md +230 -0
  58. connectonion/cli/co_ai/prompts/connectonion/debug/exceptions.md +307 -0
  59. connectonion/cli/co_ai/prompts/connectonion/debug/log.md +117 -0
  60. connectonion/cli/co_ai/prompts/connectonion/debug/xray.md +215 -0
  61. connectonion/cli/co_ai/prompts/connectonion/design-decisions/001-choosing-input-method.md +202 -0
  62. connectonion/cli/co_ai/prompts/connectonion/design-decisions/002-choosing-llm-function-name.md +202 -0
  63. connectonion/cli/co_ai/prompts/connectonion/design-decisions/003-choosing-trust-keyword.md +141 -0
  64. connectonion/cli/co_ai/prompts/connectonion/design-decisions/004-cli-create-flow.md +117 -0
  65. connectonion/cli/co_ai/prompts/connectonion/design-decisions/005-designing-agent-network-protocol.md +503 -0
  66. connectonion/cli/co_ai/prompts/connectonion/design-decisions/006-agent-address-format.md +305 -0
  67. connectonion/cli/co_ai/prompts/connectonion/design-decisions/007-authentication-backend-design.md +240 -0
  68. connectonion/cli/co_ai/prompts/connectonion/design-decisions/008-naming-is-hard.md +228 -0
  69. connectonion/cli/co_ai/prompts/connectonion/design-decisions/009-why-connect-function.md +167 -0
  70. connectonion/cli/co_ai/prompts/connectonion/design-decisions/010-cli-ux-progressive-disclosure.md +176 -0
  71. connectonion/cli/co_ai/prompts/connectonion/design-decisions/011-global-config-identity-management.md +357 -0
  72. connectonion/cli/co_ai/prompts/connectonion/design-decisions/012-tool-execution-separation.md +259 -0
  73. connectonion/cli/co_ai/prompts/connectonion/design-decisions/013-debug-and-logging-design.md +253 -0
  74. connectonion/cli/co_ai/prompts/connectonion/design-decisions/014-hook-system-design.md +510 -0
  75. connectonion/cli/co_ai/prompts/connectonion/design-decisions/015-interactive-auto-debug-design.md +837 -0
  76. connectonion/cli/co_ai/prompts/connectonion/design-decisions/016-why-no-zero-knowledge-proofs.md +358 -0
  77. connectonion/cli/co_ai/prompts/connectonion/design-decisions/017-session-logging-and-eval-format.md +120 -0
  78. connectonion/cli/co_ai/prompts/connectonion/design-decisions/018-event-api-naming.md +274 -0
  79. connectonion/cli/co_ai/prompts/connectonion/design-decisions/019-agent-lifecycle-design.md +655 -0
  80. connectonion/cli/co_ai/prompts/connectonion/design-decisions/020-trust-system-and-network-architecture.md +503 -0
  81. connectonion/cli/co_ai/prompts/connectonion/design-decisions/021-task-storage-jsonl-design.md +496 -0
  82. connectonion/cli/co_ai/prompts/connectonion/design-decisions/022-raw-asgi-implementation.md +273 -0
  83. connectonion/cli/co_ai/prompts/connectonion/examples/agent_reasoning.md +62 -0
  84. connectonion/cli/co_ai/prompts/connectonion/examples/atomic_tools.md +24 -0
  85. connectonion/cli/co_ai/prompts/connectonion/examples/load_guide.md +18 -0
  86. connectonion/cli/co_ai/prompts/connectonion/examples.md +0 -0
  87. connectonion/cli/co_ai/prompts/connectonion/hook-system-options.md +364 -0
  88. connectonion/cli/co_ai/prompts/connectonion/index.md +162 -0
  89. connectonion/cli/co_ai/prompts/connectonion/integrations/README.md +12 -0
  90. connectonion/cli/co_ai/prompts/connectonion/integrations/auth.md +450 -0
  91. connectonion/cli/co_ai/prompts/connectonion/integrations/google.md +431 -0
  92. connectonion/cli/co_ai/prompts/connectonion/integrations/microsoft.md +370 -0
  93. connectonion/cli/co_ai/prompts/connectonion/network/README.md +14 -0
  94. connectonion/cli/co_ai/prompts/connectonion/network/connect.md +543 -0
  95. connectonion/cli/co_ai/prompts/connectonion/network/connection.md +538 -0
  96. connectonion/cli/co_ai/prompts/connectonion/network/deploy.md +123 -0
  97. connectonion/cli/co_ai/prompts/connectonion/network/host.md +1049 -0
  98. connectonion/cli/co_ai/prompts/connectonion/network/protocol/agent-relay-protocol.md +495 -0
  99. connectonion/cli/co_ai/prompts/connectonion/network/protocol/announce-message.md +115 -0
  100. connectonion/cli/co_ai/prompts/connectonion/principles.md +124 -0
  101. connectonion/cli/co_ai/prompts/connectonion/quickstart.md +261 -0
  102. connectonion/cli/co_ai/prompts/connectonion/roadmap.md +81 -0
  103. connectonion/cli/co_ai/prompts/connectonion/templates/README.md +77 -0
  104. connectonion/cli/co_ai/prompts/connectonion/templates/meta-agent.md +152 -0
  105. connectonion/cli/co_ai/prompts/connectonion/templates/minimal.md +105 -0
  106. connectonion/cli/co_ai/prompts/connectonion/templates/playwright.md +130 -0
  107. connectonion/cli/co_ai/prompts/connectonion/templates/web-research.md +144 -0
  108. connectonion/cli/co_ai/prompts/connectonion/tui/README.md +95 -0
  109. connectonion/cli/co_ai/prompts/connectonion/tui/chat.md +181 -0
  110. connectonion/cli/co_ai/prompts/connectonion/tui/divider.md +63 -0
  111. connectonion/cli/co_ai/prompts/connectonion/tui/dropdown.md +83 -0
  112. connectonion/cli/co_ai/prompts/connectonion/tui/footer.md +44 -0
  113. connectonion/cli/co_ai/prompts/connectonion/tui/fuzzy.md +68 -0
  114. connectonion/cli/co_ai/prompts/connectonion/tui/input.md +84 -0
  115. connectonion/cli/co_ai/prompts/connectonion/tui/keys.md +77 -0
  116. connectonion/cli/co_ai/prompts/connectonion/tui/pick.md +71 -0
  117. connectonion/cli/co_ai/prompts/connectonion/tui/providers.md +89 -0
  118. connectonion/cli/co_ai/prompts/connectonion/tui/status_bar.md +67 -0
  119. connectonion/cli/co_ai/prompts/connectonion/useful_plugins/README.md +156 -0
  120. connectonion/cli/co_ai/prompts/connectonion/useful_plugins/calendar_plugin.md +68 -0
  121. connectonion/cli/co_ai/prompts/connectonion/useful_plugins/eval.md +89 -0
  122. connectonion/cli/co_ai/prompts/connectonion/useful_plugins/gmail_plugin.md +68 -0
  123. connectonion/cli/co_ai/prompts/connectonion/useful_plugins/image_result_formatter.md +74 -0
  124. connectonion/cli/co_ai/prompts/connectonion/useful_plugins/re_act.md +86 -0
  125. connectonion/cli/co_ai/prompts/connectonion/useful_plugins/shell_approval.md +69 -0
  126. connectonion/cli/co_ai/prompts/connectonion/useful_tools/README.md +81 -0
  127. connectonion/cli/co_ai/prompts/connectonion/useful_tools/diff_writer.md +138 -0
  128. connectonion/cli/co_ai/prompts/connectonion/useful_tools/get_emails.md +499 -0
  129. connectonion/cli/co_ai/prompts/connectonion/useful_tools/gmail.md +135 -0
  130. connectonion/cli/co_ai/prompts/connectonion/useful_tools/google_calendar.md +106 -0
  131. connectonion/cli/co_ai/prompts/connectonion/useful_tools/memory.md +486 -0
  132. connectonion/cli/co_ai/prompts/connectonion/useful_tools/microsoft_calendar.md +106 -0
  133. connectonion/cli/co_ai/prompts/connectonion/useful_tools/outlook.md +120 -0
  134. connectonion/cli/co_ai/prompts/connectonion/useful_tools/send_email.md +403 -0
  135. connectonion/cli/co_ai/prompts/connectonion/useful_tools/shell.md +95 -0
  136. connectonion/cli/co_ai/prompts/connectonion/useful_tools/slash_command.md +96 -0
  137. connectonion/cli/co_ai/prompts/connectonion/useful_tools/terminal.md +97 -0
  138. connectonion/cli/co_ai/prompts/connectonion/useful_tools/todo_list.md +252 -0
  139. connectonion/cli/co_ai/prompts/connectonion/useful_tools/web_fetch.md +130 -0
  140. connectonion/cli/co_ai/prompts/connectonion/vibe-coding-guide.md +97 -0
  141. connectonion/cli/co_ai/prompts/connectonion/windows-support.md +258 -0
  142. connectonion/cli/co_ai/prompts/main.md +247 -0
  143. connectonion/cli/co_ai/prompts/reminders/plan_mode.md +34 -0
  144. connectonion/cli/co_ai/prompts/summarization.md +55 -0
  145. connectonion/cli/co_ai/prompts/tools/ask_user.md +61 -0
  146. connectonion/cli/co_ai/prompts/tools/background.md +57 -0
  147. connectonion/cli/co_ai/prompts/tools/edit.md +90 -0
  148. connectonion/cli/co_ai/prompts/tools/glob.md +52 -0
  149. connectonion/cli/co_ai/prompts/tools/grep.md +55 -0
  150. connectonion/cli/co_ai/prompts/tools/plan_mode.md +80 -0
  151. connectonion/cli/co_ai/prompts/tools/read.md +40 -0
  152. connectonion/cli/co_ai/prompts/tools/shell.md +67 -0
  153. connectonion/cli/co_ai/prompts/tools/task.md +51 -0
  154. connectonion/cli/co_ai/prompts/tools/todo.md +139 -0
  155. connectonion/cli/co_ai/prompts/tools/write.md +47 -0
  156. connectonion/cli/co_ai/prompts/workflow.md +89 -0
  157. connectonion/cli/co_ai/reminders.py +159 -0
  158. connectonion/cli/co_ai/sessions.py +110 -0
  159. connectonion/cli/co_ai/skills/__init__.py +37 -0
  160. connectonion/cli/co_ai/skills/builtin/commit/SKILL.md +63 -0
  161. connectonion/cli/co_ai/skills/builtin/review-pr/SKILL.md +76 -0
  162. connectonion/cli/co_ai/skills/loader.py +166 -0
  163. connectonion/cli/co_ai/skills/tool.py +46 -0
  164. connectonion/cli/co_ai/tools/__init__.py +92 -0
  165. connectonion/cli/co_ai/tools/ask_user.py +35 -0
  166. connectonion/cli/co_ai/tools/background.py +201 -0
  167. connectonion/cli/co_ai/tools/diff_writer.py +291 -0
  168. connectonion/cli/co_ai/tools/edit.py +89 -0
  169. connectonion/cli/co_ai/tools/glob.py +84 -0
  170. connectonion/cli/co_ai/tools/grep.py +158 -0
  171. connectonion/cli/co_ai/tools/load_guide.py +23 -0
  172. connectonion/cli/co_ai/tools/multi_edit.py +116 -0
  173. connectonion/cli/co_ai/tools/plan_mode.py +172 -0
  174. connectonion/cli/co_ai/tools/read.py +67 -0
  175. connectonion/cli/co_ai/tools/task.py +59 -0
  176. connectonion/cli/co_ai/tools/todo_list.py +159 -0
  177. connectonion/cli/co_ai/tools/write.py +126 -0
  178. connectonion/cli/commands/__init__.py +11 -1
  179. connectonion/cli/commands/ai_commands.py +34 -0
  180. connectonion/cli/commands/copy_commands.py +55 -6
  181. connectonion/cli/commands/create.py +20 -17
  182. connectonion/cli/commands/init.py +19 -22
  183. connectonion/cli/commands/project_cmd_lib.py +15 -0
  184. connectonion/cli/main.py +11 -0
  185. connectonion/console.py +15 -1
  186. connectonion/core/__init__.py +10 -1
  187. connectonion/core/agent.py +37 -16
  188. connectonion/core/exceptions.py +74 -0
  189. connectonion/core/llm.py +54 -6
  190. connectonion/core/tool_executor.py +32 -31
  191. connectonion/core/tool_factory.py +47 -10
  192. connectonion/debug/__init__.py +10 -1
  193. connectonion/debug/debug_explainer/__init__.py +10 -1
  194. connectonion/debug/execution_analyzer/__init__.py +10 -1
  195. connectonion/debug/execution_analyzer/execution_analysis.py +5 -2
  196. connectonion/debug/runtime_inspector/__init__.py +10 -1
  197. connectonion/docs/.package-ignore +6 -0
  198. connectonion/docs/README.md +2036 -0
  199. connectonion/docs/api.md +457 -0
  200. connectonion/docs/archive/001-ai-agent-is-just-prompt-plus-function.md +249 -0
  201. connectonion/docs/archive/README.md +53 -0
  202. connectonion/docs/archive/archive/consolidation-plan.md +72 -0
  203. connectonion/docs/archive/archive/core-principles-extracted.md +239 -0
  204. connectonion/docs/archive/archive/master-principles.md +222 -0
  205. connectonion/docs/archive/archive/principles.md +293 -0
  206. connectonion/docs/archive/archive/simplicity-principles.md +221 -0
  207. connectonion/docs/archive/attack-defense-insights.md +410 -0
  208. connectonion/docs/archive/business-model.md +305 -0
  209. connectonion/docs/archive/core-principles-unified.md +190 -0
  210. connectonion/docs/archive/discussion-journey.md +178 -0
  211. connectonion/docs/archive/economic-analysis.md +323 -0
  212. connectonion/docs/archive/features/01-share-and-find.md +256 -0
  213. connectonion/docs/archive/features/02-agent-authentication.md +93 -0
  214. connectonion/docs/archive/features/03-test-before-trust.md +71 -0
  215. connectonion/docs/archive/features/06-reliability-and-offline.md +197 -0
  216. connectonion/docs/archive/features/README.md +46 -0
  217. connectonion/docs/archive/features-roadmap.md +247 -0
  218. connectonion/docs/archive/mcp-comparison-insights.md +215 -0
  219. connectonion/docs/archive/migration-strategy.md +571 -0
  220. connectonion/docs/archive/mini-whitepaper.md +293 -0
  221. connectonion/docs/archive/network-protocol.md +394 -0
  222. connectonion/docs/archive/semantic-revolution.md +367 -0
  223. connectonion/docs/archive/technical-architecture.md +453 -0
  224. connectonion/docs/archive/the-semantic-insight.md +207 -0
  225. connectonion/docs/archive/threat-model.md +164 -0
  226. connectonion/docs/cli/README.md +805 -0
  227. connectonion/docs/cli/auth.md +46 -0
  228. connectonion/docs/cli/browser.md +235 -0
  229. connectonion/docs/cli/copy.md +232 -0
  230. connectonion/docs/cli/create.md +335 -0
  231. connectonion/docs/cli/init.md +431 -0
  232. connectonion/docs/co-directory-structure.md +214 -0
  233. connectonion/docs/concepts/agent.md +1078 -0
  234. connectonion/docs/concepts/events.md +699 -0
  235. connectonion/docs/concepts/llm_do.md +256 -0
  236. connectonion/docs/concepts/max_iterations.md +362 -0
  237. connectonion/docs/concepts/models.md +641 -0
  238. connectonion/docs/concepts/plugins.md +100 -0
  239. connectonion/docs/concepts/prompts.md +122 -0
  240. connectonion/docs/concepts/session.md +428 -0
  241. connectonion/docs/concepts/tools.md +512 -0
  242. connectonion/docs/concepts/transcribe.md +156 -0
  243. connectonion/docs/concepts/trust.md +291 -0
  244. connectonion/docs/connectonion.md +1256 -0
  245. connectonion/docs/debug/README.md +18 -0
  246. connectonion/docs/debug/auto_debug.md +1026 -0
  247. connectonion/docs/debug/console.md +129 -0
  248. connectonion/docs/debug/eval-format.md +178 -0
  249. connectonion/docs/debug/eval.md +230 -0
  250. connectonion/docs/debug/exceptions.md +307 -0
  251. connectonion/docs/debug/log.md +117 -0
  252. connectonion/docs/debug/xray.md +215 -0
  253. connectonion/docs/design-decisions/001-choosing-input-method.md +202 -0
  254. connectonion/docs/design-decisions/002-choosing-llm-function-name.md +202 -0
  255. connectonion/docs/design-decisions/003-choosing-trust-keyword.md +141 -0
  256. connectonion/docs/design-decisions/004-cli-create-flow.md +117 -0
  257. connectonion/docs/design-decisions/005-designing-agent-network-protocol.md +503 -0
  258. connectonion/docs/design-decisions/006-agent-address-format.md +305 -0
  259. connectonion/docs/design-decisions/007-authentication-backend-design.md +240 -0
  260. connectonion/docs/design-decisions/008-naming-is-hard.md +228 -0
  261. connectonion/docs/design-decisions/009-why-connect-function.md +167 -0
  262. connectonion/docs/design-decisions/010-cli-ux-progressive-disclosure.md +176 -0
  263. connectonion/docs/design-decisions/011-global-config-identity-management.md +357 -0
  264. connectonion/docs/design-decisions/012-tool-execution-separation.md +259 -0
  265. connectonion/docs/design-decisions/013-debug-and-logging-design.md +253 -0
  266. connectonion/docs/design-decisions/014-hook-system-design.md +510 -0
  267. connectonion/docs/design-decisions/015-interactive-auto-debug-design.md +837 -0
  268. connectonion/docs/design-decisions/016-why-no-zero-knowledge-proofs.md +358 -0
  269. connectonion/docs/design-decisions/017-session-logging-and-eval-format.md +120 -0
  270. connectonion/docs/design-decisions/018-event-api-naming.md +274 -0
  271. connectonion/docs/design-decisions/019-agent-lifecycle-design.md +655 -0
  272. connectonion/docs/design-decisions/020-trust-system-and-network-architecture.md +503 -0
  273. connectonion/docs/design-decisions/021-task-storage-jsonl-design.md +496 -0
  274. connectonion/docs/design-decisions/022-raw-asgi-implementation.md +273 -0
  275. connectonion/docs/examples.md +0 -0
  276. connectonion/docs/hook-system-options.md +364 -0
  277. connectonion/docs/integrations/README.md +12 -0
  278. connectonion/docs/integrations/auth.md +450 -0
  279. connectonion/docs/integrations/google.md +431 -0
  280. connectonion/docs/integrations/microsoft.md +370 -0
  281. connectonion/docs/network/README.md +14 -0
  282. connectonion/docs/network/connect.md +629 -0
  283. connectonion/docs/network/deploy.md +124 -0
  284. connectonion/docs/network/host.md +1087 -0
  285. connectonion/docs/network/io.md +538 -0
  286. connectonion/docs/network/protocol/agent-relay-protocol.md +495 -0
  287. connectonion/docs/network/protocol/announce-message.md +115 -0
  288. connectonion/docs/principles.md +124 -0
  289. connectonion/docs/quickstart.md +261 -0
  290. connectonion/docs/roadmap.md +81 -0
  291. connectonion/docs/templates/README.md +77 -0
  292. connectonion/docs/templates/meta-agent.md +152 -0
  293. connectonion/docs/templates/minimal.md +105 -0
  294. connectonion/docs/templates/playwright.md +130 -0
  295. connectonion/docs/templates/web-research.md +144 -0
  296. connectonion/docs/tui/README.md +95 -0
  297. connectonion/docs/tui/chat.md +181 -0
  298. connectonion/docs/tui/divider.md +63 -0
  299. connectonion/docs/tui/dropdown.md +83 -0
  300. connectonion/docs/tui/footer.md +44 -0
  301. connectonion/docs/tui/fuzzy.md +68 -0
  302. connectonion/docs/tui/input.md +84 -0
  303. connectonion/docs/tui/keys.md +77 -0
  304. connectonion/docs/tui/pick.md +71 -0
  305. connectonion/docs/tui/providers.md +89 -0
  306. connectonion/docs/tui/status_bar.md +67 -0
  307. connectonion/docs/useful_plugins/README.md +160 -0
  308. connectonion/docs/useful_plugins/calendar_plugin.md +68 -0
  309. connectonion/docs/useful_plugins/eval.md +89 -0
  310. connectonion/docs/useful_plugins/gmail_plugin.md +68 -0
  311. connectonion/docs/useful_plugins/image_result_formatter.md +74 -0
  312. connectonion/docs/useful_plugins/re_act.md +86 -0
  313. connectonion/docs/useful_plugins/shell_approval.md +69 -0
  314. connectonion/docs/useful_plugins/system_reminder.md +210 -0
  315. connectonion/docs/useful_prompts/README.md +127 -0
  316. connectonion/docs/useful_prompts/coding_agent.md +214 -0
  317. connectonion/docs/useful_tools/README.md +81 -0
  318. connectonion/docs/useful_tools/ask_user.md +103 -0
  319. connectonion/docs/useful_tools/diff_writer.md +158 -0
  320. connectonion/docs/useful_tools/get_emails.md +519 -0
  321. connectonion/docs/useful_tools/gmail.md +155 -0
  322. connectonion/docs/useful_tools/google_calendar.md +126 -0
  323. connectonion/docs/useful_tools/memory.md +506 -0
  324. connectonion/docs/useful_tools/microsoft_calendar.md +126 -0
  325. connectonion/docs/useful_tools/outlook.md +140 -0
  326. connectonion/docs/useful_tools/send_email.md +423 -0
  327. connectonion/docs/useful_tools/shell.md +115 -0
  328. connectonion/docs/useful_tools/slash_command.md +116 -0
  329. connectonion/docs/useful_tools/terminal.md +115 -0
  330. connectonion/docs/useful_tools/todo_list.md +272 -0
  331. connectonion/docs/useful_tools/web_fetch.md +150 -0
  332. connectonion/docs/vibe-coding-guide.md +97 -0
  333. connectonion/docs/windows-support.md +258 -0
  334. connectonion/logger.py +3 -3
  335. connectonion/network/__init__.py +19 -6
  336. connectonion/network/asgi/__init__.py +81 -0
  337. connectonion/network/asgi/http.py +205 -0
  338. connectonion/network/asgi/websocket.py +217 -0
  339. connectonion/network/connect.py +232 -185
  340. connectonion/network/host/__init__.py +59 -0
  341. connectonion/network/host/auth.py +191 -0
  342. connectonion/network/host/routes.py +135 -0
  343. connectonion/network/host/server.py +289 -0
  344. connectonion/network/host/session.py +78 -0
  345. connectonion/network/io/__init__.py +21 -0
  346. connectonion/network/{connection.py → io/base.py} +17 -42
  347. connectonion/network/io/websocket.py +55 -0
  348. connectonion/network/relay.py +37 -16
  349. connectonion/network/trust/__init__.py +30 -0
  350. connectonion/network/trust/factory.py +138 -0
  351. connectonion/network/{trust_agents.py → trust/prompts.py} +3 -3
  352. connectonion/network/{trust_functions.py → trust/tools.py} +2 -2
  353. connectonion/prompt_files/__init__.py +11 -1
  354. connectonion/prompt_files/react_acknowledge.md +26 -0
  355. connectonion/prompts.py +10 -1
  356. connectonion/tui/chat.py +10 -1
  357. connectonion/tui/divider.py +10 -1
  358. connectonion/tui/dropdown.py +10 -1
  359. connectonion/tui/footer.py +8 -0
  360. connectonion/tui/fuzzy.py +11 -1
  361. connectonion/tui/input.py +118 -70
  362. connectonion/tui/keys.py +133 -6
  363. connectonion/tui/providers.py +11 -1
  364. connectonion/tui/status_bar.py +10 -1
  365. connectonion/useful_events_handlers/__init__.py +8 -0
  366. connectonion/useful_events_handlers/reflect.py +19 -4
  367. connectonion/useful_plugins/__init__.py +2 -1
  368. connectonion/useful_plugins/eval.py +2 -2
  369. connectonion/useful_plugins/gmail_plugin.py +3 -3
  370. connectonion/useful_plugins/image_result_formatter.py +3 -3
  371. connectonion/useful_plugins/re_act.py +114 -28
  372. connectonion/useful_plugins/shell_approval.py +2 -2
  373. connectonion/useful_plugins/system_reminder.py +103 -0
  374. connectonion/useful_plugins/ui_stream.py +18 -133
  375. connectonion/useful_prompts/README.md +61 -0
  376. connectonion/useful_prompts/__init__.py +45 -0
  377. connectonion/useful_prompts/coding_agent/README.md +106 -0
  378. connectonion/useful_prompts/coding_agent/assembler.py +123 -0
  379. connectonion/useful_prompts/coding_agent/prompts/main.md +227 -0
  380. connectonion/useful_prompts/coding_agent/prompts/tools/ask_user.md +61 -0
  381. connectonion/useful_prompts/coding_agent/prompts/tools/background.md +57 -0
  382. connectonion/useful_prompts/coding_agent/prompts/tools/edit.md +90 -0
  383. connectonion/useful_prompts/coding_agent/prompts/tools/glob.md +52 -0
  384. connectonion/useful_prompts/coding_agent/prompts/tools/grep.md +55 -0
  385. connectonion/useful_prompts/coding_agent/prompts/tools/plan_mode.md +80 -0
  386. connectonion/useful_prompts/coding_agent/prompts/tools/read.md +40 -0
  387. connectonion/useful_prompts/coding_agent/prompts/tools/shell.md +67 -0
  388. connectonion/useful_prompts/coding_agent/prompts/tools/task.md +51 -0
  389. connectonion/useful_prompts/coding_agent/prompts/tools/todo.md +139 -0
  390. connectonion/useful_prompts/coding_agent/prompts/tools/write.md +48 -0
  391. connectonion/useful_prompts/system-reminders/security-warning.md +14 -0
  392. connectonion/useful_prompts/system-reminders/test-reminder.md +11 -0
  393. connectonion/useful_tools/__init__.py +31 -4
  394. connectonion/useful_tools/ask_user.py +35 -0
  395. connectonion/useful_tools/bash.py +69 -0
  396. connectonion/useful_tools/diff_writer.py +186 -94
  397. connectonion/useful_tools/edit.py +102 -0
  398. connectonion/useful_tools/glob_files.py +97 -0
  399. connectonion/useful_tools/grep_files.py +171 -0
  400. connectonion/useful_tools/multi_edit.py +116 -0
  401. connectonion/useful_tools/read_file.py +73 -0
  402. connectonion/useful_tools/shell.py +50 -45
  403. connectonion/useful_tools/write_file.py +129 -0
  404. {connectonion-0.6.1.dist-info → connectonion-0.6.3.dist-info}/METADATA +10 -3
  405. connectonion-0.6.3.dist-info/RECORD +469 -0
  406. connectonion/cli/browser_agent/scroll_strategies.py +0 -276
  407. connectonion/network/asgi.py +0 -407
  408. connectonion/network/host.py +0 -616
  409. connectonion/network/trust.py +0 -166
  410. connectonion-0.6.1.dist-info/RECORD +0 -123
  411. /connectonion/cli/{docs → co_ai/prompts/connectonion}/connectonion.md +0 -0
  412. {connectonion-0.6.1.dist-info → connectonion-0.6.3.dist-info}/WHEEL +0 -0
  413. {connectonion-0.6.1.dist-info → connectonion-0.6.3.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,46 @@
1
+ """Skill tool - allows agent to invoke skills."""
2
+
3
+ from typing import Optional
4
+ from connectonion.cli.co_ai.skills.loader import get_skill, load_skills, SKILLS_REGISTRY
5
+
6
+
7
+ def skill(name: str, args: Optional[str] = None) -> str:
8
+ """
9
+ Invoke a skill by name.
10
+
11
+ Skills are specialized instruction sets that guide you through specific tasks.
12
+ When you recognize a task matches a skill's description, call this tool to
13
+ load the full instructions.
14
+
15
+ Args:
16
+ name: The skill name (e.g., "commit", "review-pr")
17
+ args: Optional arguments to pass to the skill
18
+
19
+ Returns:
20
+ The full skill instructions (SKILL.md content)
21
+
22
+ Example:
23
+ skill("commit") # Load commit instructions
24
+ skill("review-pr", args="123") # Load PR review with PR number
25
+ """
26
+ # Ensure skills are loaded
27
+ if not SKILLS_REGISTRY:
28
+ load_skills()
29
+
30
+ skill_info = get_skill(name)
31
+
32
+ if not skill_info:
33
+ available = list(SKILLS_REGISTRY.keys())
34
+ if available:
35
+ return f"Skill '{name}' not found. Available skills: {', '.join(available)}"
36
+ else:
37
+ return f"Skill '{name}' not found. No skills are currently loaded."
38
+
39
+ # Load the full skill content
40
+ content = skill_info.load_content()
41
+
42
+ # If args provided, append them
43
+ if args:
44
+ content += f"\n\n---\n## Arguments\n{args}"
45
+
46
+ return content
@@ -0,0 +1,92 @@
1
+ """
2
+ Coding tools for the AI agent (Claude Code-style).
3
+
4
+ File Tools:
5
+ - read_file: Read file with line numbers
6
+ - edit: Precise string replacement (str_replace)
7
+ - multi_edit: Multiple atomic string replacements
8
+ - write: Full file overwrite with approval
9
+ - Write: Class-based write with mode control
10
+
11
+ Search Tools:
12
+ - glob: Find files by pattern
13
+ - grep: Search file contents
14
+
15
+ Task Tools:
16
+ - task: Spawn sub-agent for complex tasks
17
+ - run_background: Run command in background
18
+ - task_output: Get background task output
19
+ - kill_task: Stop background task
20
+
21
+ Planning Tools:
22
+ - enter_plan_mode: Switch to planning mode
23
+ - exit_plan_mode: Exit planning mode
24
+ - write_plan: Write plan content
25
+
26
+ Interaction Tools:
27
+ - ask_user: Ask user a question via io
28
+ - load_guide: Load documentation/guide
29
+
30
+ Utility Classes:
31
+ - DiffWriter: Low-level file writer with diff preview
32
+ - TodoList: Task list management
33
+
34
+ Note: File tools are re-exported from connectonion.useful_tools for consistency.
35
+ """
36
+
37
+ # File tools (Claude Code-style) - re-export from useful_tools
38
+ from connectonion.useful_tools import (
39
+ read_file,
40
+ edit,
41
+ multi_edit,
42
+ glob,
43
+ grep,
44
+ write,
45
+ FileWriter,
46
+ DiffWriter,
47
+ MODE_NORMAL,
48
+ MODE_AUTO,
49
+ MODE_PLAN,
50
+ TodoList,
51
+ )
52
+
53
+ # Task tools (CLI-specific)
54
+ from connectonion.cli.co_ai.tools.task import task
55
+ from connectonion.cli.co_ai.tools.background import run_background, task_output, kill_task
56
+
57
+ # Planning tools (CLI-specific)
58
+ from connectonion.cli.co_ai.tools.plan_mode import enter_plan_mode, exit_plan_mode, write_plan
59
+
60
+ # Interaction tools (CLI-specific)
61
+ from connectonion.cli.co_ai.tools.ask_user import ask_user
62
+ from connectonion.cli.co_ai.tools.load_guide import load_guide
63
+
64
+ __all__ = [
65
+ # File tools (Claude Code-style)
66
+ "read_file",
67
+ "edit",
68
+ "multi_edit",
69
+ "write",
70
+ "FileWriter",
71
+ "DiffWriter",
72
+ "MODE_NORMAL",
73
+ "MODE_AUTO",
74
+ "MODE_PLAN",
75
+ # Search tools
76
+ "glob",
77
+ "grep",
78
+ # Task tools
79
+ "task",
80
+ "run_background",
81
+ "task_output",
82
+ "kill_task",
83
+ # Planning tools
84
+ "enter_plan_mode",
85
+ "exit_plan_mode",
86
+ "write_plan",
87
+ # Interaction tools
88
+ "ask_user",
89
+ "load_guide",
90
+ # Utility classes
91
+ "TodoList",
92
+ ]
@@ -0,0 +1,35 @@
1
+ """
2
+ Purpose: Ask user a question during agent execution via connection
3
+ LLM-Note:
4
+ Dependencies: imports from [typing] | imported by [useful_tools/__init__.py]
5
+ Data flow: agent calls ask_user tool → sends ask_user event via connection → waits for response → returns answer
6
+ State/Effects: blocks until user responds via connection
7
+ Integration: requires agent.connection to be set | agent parameter injected by tool_executor
8
+ """
9
+
10
+ from typing import List, Optional
11
+
12
+
13
+ def ask_user(
14
+ agent,
15
+ question: str,
16
+ options: Optional[List[str]] = None,
17
+ multi_select: bool = False
18
+ ) -> str:
19
+ """Ask the user a question and wait for their response.
20
+
21
+ Args:
22
+ question: The question to ask the user
23
+ options: Optional list of choices for the user to select from
24
+ multi_select: If True, user can select multiple options
25
+
26
+ Returns:
27
+ The user's answer (or comma-separated answers if multi_select)
28
+ """
29
+ agent.connection.send({
30
+ "type": "ask_user",
31
+ "question": question,
32
+ "options": options,
33
+ "multi_select": multi_select
34
+ })
35
+ return agent.connection.receive().get("answer", "")
@@ -0,0 +1,201 @@
1
+ """Background task execution for long-running operations."""
2
+
3
+ import subprocess
4
+ import threading
5
+ import time
6
+ from dataclasses import dataclass, field
7
+ from typing import Dict, Optional
8
+ from enum import Enum
9
+
10
+
11
+ class TaskStatus(Enum):
12
+ RUNNING = "running"
13
+ COMPLETED = "completed"
14
+ FAILED = "failed"
15
+
16
+
17
+ @dataclass
18
+ class BackgroundTask:
19
+ """A background task with its process and output."""
20
+ id: str
21
+ command: str
22
+ process: subprocess.Popen
23
+ output: list = field(default_factory=list)
24
+ status: TaskStatus = TaskStatus.RUNNING
25
+ start_time: float = field(default_factory=time.time)
26
+ end_time: Optional[float] = None
27
+
28
+
29
+ # Global task registry
30
+ _tasks: Dict[str, BackgroundTask] = {}
31
+ _task_counter = 0
32
+ _lock = threading.Lock()
33
+
34
+
35
+ def _reset_for_testing():
36
+ """Reset state for testing. Not for production use."""
37
+ global _tasks, _task_counter
38
+ with _lock:
39
+ for task in _tasks.values():
40
+ if task.status == TaskStatus.RUNNING:
41
+ task.process.terminate()
42
+ _tasks.clear()
43
+ _task_counter = 0
44
+
45
+
46
+ def _read_output(task: BackgroundTask):
47
+ """Read output from process in background thread."""
48
+ for line in iter(task.process.stdout.readline, ""):
49
+ if not line:
50
+ break
51
+ task.output.append(line.rstrip())
52
+
53
+ task.process.wait()
54
+ task.end_time = time.time()
55
+ task.status = TaskStatus.COMPLETED if task.process.returncode == 0 else TaskStatus.FAILED
56
+
57
+
58
+ def run_background(command: str, description: str = "") -> str:
59
+ """
60
+ Run a shell command in the background.
61
+
62
+ Use this for long-running operations like builds, tests, or servers.
63
+ Returns immediately with a task ID. Use task_output() to check results.
64
+
65
+ Args:
66
+ command: Shell command to run (e.g., "npm run build", "pytest")
67
+ description: Optional description for the task
68
+
69
+ Returns:
70
+ Task ID and confirmation message
71
+
72
+ Example:
73
+ run_background("npm run build") # Returns: "Task bg_1 started: npm run build"
74
+ task_output("bg_1") # Check output later
75
+ """
76
+ global _task_counter
77
+
78
+ with _lock:
79
+ _task_counter += 1
80
+ task_id = f"bg_{_task_counter}"
81
+
82
+ process = subprocess.Popen(
83
+ command,
84
+ shell=True,
85
+ stdout=subprocess.PIPE,
86
+ stderr=subprocess.STDOUT,
87
+ text=True,
88
+ bufsize=1,
89
+ )
90
+
91
+ task = BackgroundTask(
92
+ id=task_id,
93
+ command=command,
94
+ process=process,
95
+ )
96
+
97
+ with _lock:
98
+ _tasks[task_id] = task
99
+
100
+ # Start output reader thread
101
+ thread = threading.Thread(target=_read_output, args=(task,), daemon=True)
102
+ thread.start()
103
+
104
+ desc = f" ({description})" if description else ""
105
+ return f"Task {task_id} started{desc}: {command}"
106
+
107
+
108
+ def task_output(task_id: str, tail: int = 50) -> str:
109
+ """
110
+ Get output from a background task.
111
+
112
+ Args:
113
+ task_id: The task ID (e.g., "bg_1")
114
+ tail: Number of recent lines to show (default: 50)
115
+
116
+ Returns:
117
+ Task status and output
118
+
119
+ Example:
120
+ task_output("bg_1") # Get output from task bg_1
121
+ task_output("bg_1", tail=100) # Get last 100 lines
122
+ """
123
+ with _lock:
124
+ task = _tasks.get(task_id)
125
+
126
+ if not task:
127
+ available = list(_tasks.keys())
128
+ if available:
129
+ return f"Task '{task_id}' not found. Available: {', '.join(available)}"
130
+ return f"Task '{task_id}' not found. No background tasks running."
131
+
132
+ elapsed = time.time() - task.start_time
133
+ if task.end_time:
134
+ elapsed = task.end_time - task.start_time
135
+
136
+ status_line = f"Task {task_id}: {task.status.value} ({elapsed:.1f}s)"
137
+ status_line += f"\nCommand: {task.command}"
138
+
139
+ if task.status == TaskStatus.FAILED:
140
+ status_line += f"\nExit code: {task.process.returncode}"
141
+
142
+ output_lines = task.output[-tail:] if task.output else []
143
+ if output_lines:
144
+ output_text = "\n".join(output_lines)
145
+ if len(task.output) > tail:
146
+ output_text = f"... ({len(task.output) - tail} lines omitted)\n{output_text}"
147
+ return f"{status_line}\n\nOutput:\n{output_text}"
148
+
149
+ return f"{status_line}\n\n(no output yet)"
150
+
151
+
152
+ def kill_task(task_id: str) -> str:
153
+ """
154
+ Kill a running background task.
155
+
156
+ Args:
157
+ task_id: The task ID to kill (e.g., "bg_1")
158
+
159
+ Returns:
160
+ Confirmation message
161
+ """
162
+ with _lock:
163
+ task = _tasks.get(task_id)
164
+
165
+ if not task:
166
+ return f"Task '{task_id}' not found."
167
+
168
+ if task.status != TaskStatus.RUNNING:
169
+ return f"Task '{task_id}' is not running (status: {task.status.value})"
170
+
171
+ task.process.terminate()
172
+ task.status = TaskStatus.FAILED
173
+ task.end_time = time.time()
174
+
175
+ return f"Task '{task_id}' terminated."
176
+
177
+
178
+ def list_tasks() -> str:
179
+ """
180
+ List all background tasks.
181
+
182
+ Returns:
183
+ Table of all tasks with their status
184
+ """
185
+ with _lock:
186
+ tasks = list(_tasks.values())
187
+
188
+ if not tasks:
189
+ return "No background tasks."
190
+
191
+ lines = ["Background Tasks:", ""]
192
+ for t in tasks:
193
+ elapsed = time.time() - t.start_time
194
+ if t.end_time:
195
+ elapsed = t.end_time - t.start_time
196
+
197
+ status_icon = {"running": "⏳", "completed": "✓", "failed": "✗"}[t.status.value]
198
+ cmd_short = t.command[:40] + "..." if len(t.command) > 40 else t.command
199
+ lines.append(f" {status_icon} {t.id}: {cmd_short} ({elapsed:.1f}s)")
200
+
201
+ return "\n".join(lines)
@@ -0,0 +1,291 @@
1
+ """
2
+ Purpose: Web-based file writing tool with Claude Code-style permission modes
3
+ LLM-Note:
4
+ Dependencies: imports from [difflib, pathlib, typing] | imported by [co_ai.tools.__init__]
5
+ Data flow: Agent calls DiffWriter.write(path, content) -> show diff via io -> ask approval via io -> write file -> return status
6
+ State/Effects: reads and writes files on filesystem | sends diff_preview and ask_user events via io | requires io channel for approval
7
+ Integration: exposes DiffWriter class with write(path, content), diff(path, content), read(path) | used as agent tool via Agent(tools=[DiffWriter()])
8
+ Errors: returns error string if file unreadable | returns user feedback on rejection | no exceptions raised
9
+
10
+ Permission Modes (like Claude Code's Shift+Tab cycle):
11
+ - normal: Prompt for every edit (default)
12
+ - auto: Auto-approve all edits without prompting
13
+ - plan: Read-only mode, no writes allowed
14
+
15
+ Architecture:
16
+
17
+ Agent Thread WebSocketIO Browser
18
+ │ │ │
19
+ │ 1. write("app.py", code) │ │
20
+ │ │ │
21
+ │ 2. io.send({type: "diff_preview", path, diff, ...}) │
22
+ │─────────────────────────────▶│─────────────────────────────▶
23
+ │ │ │
24
+ │ 3. io.send({type: "ask_user", question, options}) │
25
+ │─────────────────────────────▶│─────────────────────────────▶
26
+ │ │ │
27
+ │ 4. io.receive() [BLOCKS] │ User clicks option │
28
+ │◀─────────────────────────────│◀────────────────────────────│
29
+ │ │ │
30
+ │ 5. Write file or return rejection with feedback │
31
+ │ │ │
32
+ """
33
+
34
+ import difflib
35
+ from pathlib import Path
36
+ from typing import Optional, Tuple
37
+
38
+
39
+ # Permission modes (like Claude Code's Shift+Tab cycle)
40
+ MODE_NORMAL = "normal" # Prompt for every edit
41
+ MODE_AUTO = "auto" # Auto-approve edits
42
+ MODE_PLAN = "plan" # Read-only, no writes
43
+
44
+
45
+ class DiffWriter:
46
+ """File writer with Claude Code-style permission modes (web mode).
47
+
48
+ Requires io channel for approval prompts. Without io, falls back to auto-approve.
49
+
50
+ Usage:
51
+ writer = DiffWriter(mode="normal") # Prompt for every edit
52
+ writer = DiffWriter(mode="auto") # Auto-approve all edits
53
+ writer = DiffWriter(mode="plan") # Read-only, preview only
54
+ """
55
+
56
+ def __init__(self, mode: str = MODE_NORMAL, preview_limit: int = 2000):
57
+ """Initialize DiffWriter.
58
+
59
+ Args:
60
+ mode: Permission mode - "normal" (prompt), "auto" (auto-approve), "plan" (read-only)
61
+ preview_limit: Max chars to include in diff preview
62
+ """
63
+ self.mode = mode
64
+ self.preview_limit = preview_limit
65
+ self.io = None # Set by agent's _sync_tool_io event handler
66
+
67
+ def write(self, path: str, content: str) -> str:
68
+ """Write content to a file with diff display and approval.
69
+
70
+ Args:
71
+ path: File path to write to
72
+ content: Content to write
73
+
74
+ Returns:
75
+ Success message, rejection message with feedback, or plan mode preview
76
+ """
77
+ file_path = Path(path)
78
+ file_exists = file_path.exists()
79
+
80
+ # Plan mode = read-only, just show what would happen
81
+ if self.mode == MODE_PLAN:
82
+ diff_text = self._generate_diff(path, content)
83
+ preview = self._build_preview(diff_text, content, file_exists)
84
+ return f"[Plan mode] Would write {len(content)} bytes to {path}\n\nPreview:\n{preview[:500]}"
85
+
86
+ # Generate diff for display
87
+ diff_text = self._generate_diff(path, content)
88
+ preview = self._build_preview(diff_text, content, file_exists)
89
+ preview, truncated = self._truncate_preview(preview)
90
+
91
+ # Send diff preview to UI (best-effort, doesn't block)
92
+ self._send_preview(path, preview, truncated, file_exists)
93
+
94
+ # Check approval based on mode
95
+ if self.mode == MODE_NORMAL:
96
+ choice = self._ask_approval(path, preview, truncated)
97
+
98
+ if choice == "reject":
99
+ feedback = self._ask_feedback(path)
100
+ return f"User rejected changes to {path}. Feedback: {feedback}"
101
+
102
+ if choice == "approve_all":
103
+ self.mode = MODE_AUTO # Switch to auto mode for rest of session
104
+
105
+ # Auto mode or approved: write file
106
+ file_path.parent.mkdir(parents=True, exist_ok=True)
107
+ file_path.write_text(content, encoding="utf-8")
108
+
109
+ mode_note = "" if self.mode == MODE_NORMAL else f" [{self.mode} mode]"
110
+ return f"Wrote {len(content)} bytes to {path}{mode_note}"
111
+
112
+ def diff(self, path: str, content: str) -> str:
113
+ """Show diff without writing (preview mode).
114
+
115
+ Args:
116
+ path: File path to compare against
117
+ content: New content to compare
118
+
119
+ Returns:
120
+ Diff string in unified format
121
+ """
122
+ diff_text = self._generate_diff(path, content)
123
+ if diff_text:
124
+ return diff_text
125
+ return f"No changes to {path}"
126
+
127
+ def read(self, path: str) -> str:
128
+ """Read file contents.
129
+
130
+ Args:
131
+ path: File path to read
132
+
133
+ Returns:
134
+ File contents or error message
135
+ """
136
+ file_path = Path(path)
137
+ if not file_path.exists():
138
+ return f"Error: File {path} not found"
139
+ return file_path.read_text(encoding="utf-8")
140
+
141
+ # =========================================================================
142
+ # IO Communication (Web Mode)
143
+ # =========================================================================
144
+
145
+ def _get_io(self):
146
+ """Return active io channel if available."""
147
+ return self.io
148
+
149
+ def _send_preview(self, path: str, preview: str, truncated: bool, file_exists: bool) -> None:
150
+ """Send diff preview event to UI client.
151
+
152
+ This is informational - UI can render a nice diff view.
153
+ Does not block or wait for response.
154
+ """
155
+ io = self._get_io()
156
+ if not io:
157
+ return
158
+ io.send({
159
+ "type": "diff_preview",
160
+ "path": path,
161
+ "preview": preview,
162
+ "truncated": truncated,
163
+ "file_exists": file_exists,
164
+ })
165
+
166
+ def _ask_approval(self, path: str, preview: str, truncated: bool) -> str:
167
+ """Ask user for approval via io channel.
168
+
169
+ Returns:
170
+ "approve" - Apply this change
171
+ "approve_all" - Apply and switch to auto mode
172
+ "reject" - Reject and ask for feedback
173
+ """
174
+ io = self._get_io()
175
+ if not io:
176
+ # No io channel = auto-approve (for non-web usage)
177
+ return "approve"
178
+
179
+ question = f"Apply changes to {path}?"
180
+ if truncated:
181
+ question += " (preview truncated)"
182
+
183
+ response = self._ask_user(
184
+ question,
185
+ options=[
186
+ "Yes, apply this change",
187
+ "Yes to all (auto-approve)",
188
+ "No, reject and give feedback",
189
+ ],
190
+ )
191
+
192
+ # Parse response
193
+ if response == "Yes to all (auto-approve)":
194
+ return "approve_all"
195
+ if response == "Yes, apply this change":
196
+ return "approve"
197
+
198
+ # Handle free-text responses
199
+ if response:
200
+ lowered = response.strip().lower()
201
+ if "yes to all" in lowered or lowered == "auto":
202
+ return "approve_all"
203
+ if lowered.startswith("yes") or lowered == "approve":
204
+ return "approve"
205
+
206
+ return "reject"
207
+
208
+ def _ask_feedback(self, path: str) -> str:
209
+ """Ask user for feedback when changes are rejected."""
210
+ feedback = self._ask_user(f"What should the agent do instead for {path}?")
211
+ return feedback or "No feedback provided"
212
+
213
+ def _ask_user(self, question: str, options: Optional[list] = None) -> str:
214
+ """Send ask_user event and block until user responds.
215
+
216
+ Args:
217
+ question: Question to display
218
+ options: List of option strings (buttons in UI)
219
+
220
+ Returns:
221
+ User's answer string
222
+ """
223
+ io = self._get_io()
224
+ if not io:
225
+ return ""
226
+
227
+ # Send ask_user event
228
+ io.send({
229
+ "type": "ask_user",
230
+ "question": question,
231
+ "options": options,
232
+ })
233
+
234
+ # Block waiting for response
235
+ response = io.receive()
236
+
237
+ # Handle connection closed
238
+ if response.get("type") == "io_closed":
239
+ return ""
240
+
241
+ # Extract answer
242
+ answer = response.get("answer", "")
243
+ if isinstance(answer, list):
244
+ return ", ".join([str(a) for a in answer])
245
+ return str(answer) if answer is not None else ""
246
+
247
+ # =========================================================================
248
+ # Diff Generation
249
+ # =========================================================================
250
+
251
+ def _generate_diff(self, path: str, new_content: str) -> str:
252
+ """Generate unified diff between existing file and new content."""
253
+ file_path = Path(path)
254
+
255
+ if not file_path.exists():
256
+ return "" # New file, no diff to show
257
+
258
+ original_lines = file_path.read_text(encoding="utf-8").splitlines(keepends=True)
259
+ new_lines = new_content.splitlines(keepends=True)
260
+
261
+ diff = difflib.unified_diff(
262
+ original_lines,
263
+ new_lines,
264
+ fromfile=f"a/{path}",
265
+ tofile=f"b/{path}",
266
+ )
267
+
268
+ return "".join(diff)
269
+
270
+ def _build_preview(self, diff_text: str, content: str, file_exists: bool) -> str:
271
+ """Build human-readable preview for approval dialog."""
272
+ if diff_text:
273
+ return diff_text
274
+ if file_exists:
275
+ return "(no changes)"
276
+ return self._new_file_preview(content)
277
+
278
+ def _truncate_preview(self, preview: str) -> Tuple[str, bool]:
279
+ """Truncate preview to configured limit."""
280
+ if len(preview) <= self.preview_limit:
281
+ return preview, False
282
+ return preview[: self.preview_limit] + "\n...(truncated)", True
283
+
284
+ def _new_file_preview(self, content: str, max_lines: int = 50) -> str:
285
+ """Create preview for new file (no existing file to diff against)."""
286
+ lines = content.splitlines()
287
+ preview_lines = lines[:max_lines]
288
+ preview = "\n".join([f"+ {line}" for line in preview_lines])
289
+ if len(lines) > max_lines:
290
+ preview += f"\n... ({len(lines) - max_lines} more lines)"
291
+ return preview