connectonion 0.6.2__py3-none-any.whl → 0.6.4__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 (410) 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 +13 -3
  5. connectonion/cli/browser_agent/element_finder.py +8 -0
  6. connectonion/cli/browser_agent/highlight_screenshot.py +9 -1
  7. connectonion/cli/browser_agent/scroll.py +8 -0
  8. connectonion/cli/co_ai/__init__.py +6 -0
  9. connectonion/cli/co_ai/agent.py +87 -0
  10. connectonion/cli/co_ai/agents/__init__.py +5 -0
  11. connectonion/cli/co_ai/agents/registry.py +57 -0
  12. connectonion/cli/co_ai/commands/__init__.py +45 -0
  13. connectonion/cli/co_ai/commands/compact.py +173 -0
  14. connectonion/cli/co_ai/commands/cost.py +77 -0
  15. connectonion/cli/co_ai/commands/export.py +60 -0
  16. connectonion/cli/co_ai/commands/help.py +80 -0
  17. connectonion/cli/co_ai/commands/init.py +101 -0
  18. connectonion/cli/co_ai/commands/sessions.py +55 -0
  19. connectonion/cli/co_ai/commands/tasks.py +63 -0
  20. connectonion/cli/co_ai/commands/undo.py +103 -0
  21. connectonion/cli/co_ai/context.py +127 -0
  22. connectonion/cli/co_ai/main.py +52 -0
  23. connectonion/cli/co_ai/plugins/__init__.py +5 -0
  24. connectonion/cli/co_ai/plugins/system_reminder.py +154 -0
  25. connectonion/cli/co_ai/prompts/agents/explore.md +79 -0
  26. connectonion/cli/co_ai/prompts/agents/plan.md +60 -0
  27. connectonion/cli/co_ai/prompts/assembler.py +303 -0
  28. connectonion/cli/{docs/co-vibecoding-principles-docs-contexts-all-in-one.md → co_ai/prompts/connectonion/README.md} +26 -0
  29. connectonion/cli/co_ai/prompts/connectonion/api.md +457 -0
  30. connectonion/cli/co_ai/prompts/connectonion/cli/README.md +805 -0
  31. connectonion/cli/co_ai/prompts/connectonion/cli/auth.md +46 -0
  32. connectonion/cli/co_ai/prompts/connectonion/cli/browser.md +235 -0
  33. connectonion/cli/co_ai/prompts/connectonion/cli/copy.md +184 -0
  34. connectonion/cli/co_ai/prompts/connectonion/cli/create.md +335 -0
  35. connectonion/cli/co_ai/prompts/connectonion/cli/init.md +431 -0
  36. connectonion/cli/co_ai/prompts/connectonion/co-directory-structure.md +214 -0
  37. connectonion/cli/co_ai/prompts/connectonion/concepts/agent.md +1078 -0
  38. connectonion/cli/co_ai/prompts/connectonion/concepts/events.md +816 -0
  39. connectonion/cli/co_ai/prompts/connectonion/concepts/llm_do.md +256 -0
  40. connectonion/cli/co_ai/prompts/connectonion/concepts/max_iterations.md +362 -0
  41. connectonion/cli/co_ai/prompts/connectonion/concepts/models.md +641 -0
  42. connectonion/cli/co_ai/prompts/connectonion/concepts/plugins.md +100 -0
  43. connectonion/cli/co_ai/prompts/connectonion/concepts/prompts.md +122 -0
  44. connectonion/cli/co_ai/prompts/connectonion/concepts/tools.md +512 -0
  45. connectonion/cli/co_ai/prompts/connectonion/concepts/transcribe.md +156 -0
  46. connectonion/cli/co_ai/prompts/connectonion/concepts/trust.md +291 -0
  47. connectonion/cli/co_ai/prompts/connectonion/debug/README.md +18 -0
  48. connectonion/cli/co_ai/prompts/connectonion/debug/auto_debug.md +1026 -0
  49. connectonion/cli/co_ai/prompts/connectonion/debug/console.md +129 -0
  50. connectonion/cli/co_ai/prompts/connectonion/debug/eval-format.md +178 -0
  51. connectonion/cli/co_ai/prompts/connectonion/debug/eval.md +230 -0
  52. connectonion/cli/co_ai/prompts/connectonion/debug/exceptions.md +307 -0
  53. connectonion/cli/co_ai/prompts/connectonion/debug/log.md +117 -0
  54. connectonion/cli/co_ai/prompts/connectonion/debug/xray.md +215 -0
  55. connectonion/cli/co_ai/prompts/connectonion/design-decisions/001-choosing-input-method.md +202 -0
  56. connectonion/cli/co_ai/prompts/connectonion/design-decisions/002-choosing-llm-function-name.md +202 -0
  57. connectonion/cli/co_ai/prompts/connectonion/design-decisions/003-choosing-trust-keyword.md +141 -0
  58. connectonion/cli/co_ai/prompts/connectonion/design-decisions/004-cli-create-flow.md +117 -0
  59. connectonion/cli/co_ai/prompts/connectonion/design-decisions/005-designing-agent-network-protocol.md +503 -0
  60. connectonion/cli/co_ai/prompts/connectonion/design-decisions/006-agent-address-format.md +305 -0
  61. connectonion/cli/co_ai/prompts/connectonion/design-decisions/007-authentication-backend-design.md +240 -0
  62. connectonion/cli/co_ai/prompts/connectonion/design-decisions/008-naming-is-hard.md +228 -0
  63. connectonion/cli/co_ai/prompts/connectonion/design-decisions/009-why-connect-function.md +167 -0
  64. connectonion/cli/co_ai/prompts/connectonion/design-decisions/010-cli-ux-progressive-disclosure.md +176 -0
  65. connectonion/cli/co_ai/prompts/connectonion/design-decisions/011-global-config-identity-management.md +357 -0
  66. connectonion/cli/co_ai/prompts/connectonion/design-decisions/012-tool-execution-separation.md +259 -0
  67. connectonion/cli/co_ai/prompts/connectonion/design-decisions/013-debug-and-logging-design.md +253 -0
  68. connectonion/cli/co_ai/prompts/connectonion/design-decisions/014-hook-system-design.md +510 -0
  69. connectonion/cli/co_ai/prompts/connectonion/design-decisions/015-interactive-auto-debug-design.md +837 -0
  70. connectonion/cli/co_ai/prompts/connectonion/design-decisions/016-why-no-zero-knowledge-proofs.md +358 -0
  71. connectonion/cli/co_ai/prompts/connectonion/design-decisions/017-session-logging-and-eval-format.md +120 -0
  72. connectonion/cli/co_ai/prompts/connectonion/design-decisions/018-event-api-naming.md +274 -0
  73. connectonion/cli/co_ai/prompts/connectonion/design-decisions/019-agent-lifecycle-design.md +655 -0
  74. connectonion/cli/co_ai/prompts/connectonion/design-decisions/020-trust-system-and-network-architecture.md +503 -0
  75. connectonion/cli/co_ai/prompts/connectonion/design-decisions/021-task-storage-jsonl-design.md +496 -0
  76. connectonion/cli/co_ai/prompts/connectonion/design-decisions/022-raw-asgi-implementation.md +273 -0
  77. connectonion/cli/co_ai/prompts/connectonion/examples/agent_reasoning.md +62 -0
  78. connectonion/cli/co_ai/prompts/connectonion/examples/atomic_tools.md +24 -0
  79. connectonion/cli/co_ai/prompts/connectonion/examples/load_guide.md +18 -0
  80. connectonion/cli/co_ai/prompts/connectonion/examples.md +0 -0
  81. connectonion/cli/co_ai/prompts/connectonion/hook-system-options.md +364 -0
  82. connectonion/cli/co_ai/prompts/connectonion/index.md +162 -0
  83. connectonion/cli/co_ai/prompts/connectonion/integrations/README.md +12 -0
  84. connectonion/cli/co_ai/prompts/connectonion/integrations/auth.md +450 -0
  85. connectonion/cli/co_ai/prompts/connectonion/integrations/google.md +431 -0
  86. connectonion/cli/co_ai/prompts/connectonion/integrations/microsoft.md +370 -0
  87. connectonion/cli/co_ai/prompts/connectonion/network/README.md +14 -0
  88. connectonion/cli/co_ai/prompts/connectonion/network/connect.md +543 -0
  89. connectonion/cli/co_ai/prompts/connectonion/network/connection.md +538 -0
  90. connectonion/cli/co_ai/prompts/connectonion/network/deploy.md +123 -0
  91. connectonion/cli/co_ai/prompts/connectonion/network/host.md +1049 -0
  92. connectonion/cli/co_ai/prompts/connectonion/network/protocol/agent-relay-protocol.md +495 -0
  93. connectonion/cli/co_ai/prompts/connectonion/network/protocol/announce-message.md +115 -0
  94. connectonion/cli/co_ai/prompts/connectonion/principles.md +124 -0
  95. connectonion/cli/co_ai/prompts/connectonion/quickstart.md +261 -0
  96. connectonion/cli/co_ai/prompts/connectonion/roadmap.md +81 -0
  97. connectonion/cli/co_ai/prompts/connectonion/templates/README.md +77 -0
  98. connectonion/cli/co_ai/prompts/connectonion/templates/meta-agent.md +152 -0
  99. connectonion/cli/co_ai/prompts/connectonion/templates/minimal.md +105 -0
  100. connectonion/cli/co_ai/prompts/connectonion/templates/playwright.md +130 -0
  101. connectonion/cli/co_ai/prompts/connectonion/templates/web-research.md +144 -0
  102. connectonion/cli/co_ai/prompts/connectonion/tui/README.md +95 -0
  103. connectonion/cli/co_ai/prompts/connectonion/tui/chat.md +181 -0
  104. connectonion/cli/co_ai/prompts/connectonion/tui/divider.md +63 -0
  105. connectonion/cli/co_ai/prompts/connectonion/tui/dropdown.md +83 -0
  106. connectonion/cli/co_ai/prompts/connectonion/tui/footer.md +44 -0
  107. connectonion/cli/co_ai/prompts/connectonion/tui/fuzzy.md +68 -0
  108. connectonion/cli/co_ai/prompts/connectonion/tui/input.md +84 -0
  109. connectonion/cli/co_ai/prompts/connectonion/tui/keys.md +77 -0
  110. connectonion/cli/co_ai/prompts/connectonion/tui/pick.md +71 -0
  111. connectonion/cli/co_ai/prompts/connectonion/tui/providers.md +89 -0
  112. connectonion/cli/co_ai/prompts/connectonion/tui/status_bar.md +67 -0
  113. connectonion/cli/co_ai/prompts/connectonion/useful_plugins/README.md +156 -0
  114. connectonion/cli/co_ai/prompts/connectonion/useful_plugins/calendar_plugin.md +68 -0
  115. connectonion/cli/co_ai/prompts/connectonion/useful_plugins/eval.md +89 -0
  116. connectonion/cli/co_ai/prompts/connectonion/useful_plugins/gmail_plugin.md +68 -0
  117. connectonion/cli/co_ai/prompts/connectonion/useful_plugins/image_result_formatter.md +74 -0
  118. connectonion/cli/co_ai/prompts/connectonion/useful_plugins/re_act.md +86 -0
  119. connectonion/cli/co_ai/prompts/connectonion/useful_plugins/shell_approval.md +69 -0
  120. connectonion/cli/co_ai/prompts/connectonion/useful_tools/README.md +81 -0
  121. connectonion/cli/co_ai/prompts/connectonion/useful_tools/diff_writer.md +138 -0
  122. connectonion/cli/co_ai/prompts/connectonion/useful_tools/get_emails.md +499 -0
  123. connectonion/cli/co_ai/prompts/connectonion/useful_tools/gmail.md +135 -0
  124. connectonion/cli/co_ai/prompts/connectonion/useful_tools/google_calendar.md +106 -0
  125. connectonion/cli/co_ai/prompts/connectonion/useful_tools/memory.md +486 -0
  126. connectonion/cli/co_ai/prompts/connectonion/useful_tools/microsoft_calendar.md +106 -0
  127. connectonion/cli/co_ai/prompts/connectonion/useful_tools/outlook.md +120 -0
  128. connectonion/cli/co_ai/prompts/connectonion/useful_tools/send_email.md +403 -0
  129. connectonion/cli/co_ai/prompts/connectonion/useful_tools/shell.md +95 -0
  130. connectonion/cli/co_ai/prompts/connectonion/useful_tools/slash_command.md +96 -0
  131. connectonion/cli/co_ai/prompts/connectonion/useful_tools/terminal.md +97 -0
  132. connectonion/cli/co_ai/prompts/connectonion/useful_tools/todo_list.md +252 -0
  133. connectonion/cli/co_ai/prompts/connectonion/useful_tools/web_fetch.md +130 -0
  134. connectonion/cli/co_ai/prompts/connectonion/vibe-coding-guide.md +97 -0
  135. connectonion/cli/co_ai/prompts/connectonion/windows-support.md +258 -0
  136. connectonion/cli/co_ai/prompts/main.md +247 -0
  137. connectonion/cli/co_ai/prompts/summarization.md +55 -0
  138. connectonion/cli/co_ai/prompts/system-reminders/agent.md +23 -0
  139. connectonion/cli/co_ai/prompts/system-reminders/plan_mode.md +13 -0
  140. connectonion/cli/co_ai/prompts/system-reminders/security.md +14 -0
  141. connectonion/cli/co_ai/prompts/system-reminders/simplicity.md +14 -0
  142. connectonion/cli/co_ai/prompts/tools/ask_user.md +61 -0
  143. connectonion/cli/co_ai/prompts/tools/background.md +57 -0
  144. connectonion/cli/co_ai/prompts/tools/edit.md +90 -0
  145. connectonion/cli/co_ai/prompts/tools/glob.md +52 -0
  146. connectonion/cli/co_ai/prompts/tools/grep.md +55 -0
  147. connectonion/cli/co_ai/prompts/tools/plan_mode.md +80 -0
  148. connectonion/cli/co_ai/prompts/tools/read.md +40 -0
  149. connectonion/cli/co_ai/prompts/tools/shell.md +67 -0
  150. connectonion/cli/co_ai/prompts/tools/task.md +51 -0
  151. connectonion/cli/co_ai/prompts/tools/todo.md +139 -0
  152. connectonion/cli/co_ai/prompts/tools/write.md +47 -0
  153. connectonion/cli/co_ai/prompts/workflow.md +89 -0
  154. connectonion/cli/co_ai/sessions.py +110 -0
  155. connectonion/cli/co_ai/skills/__init__.py +37 -0
  156. connectonion/cli/co_ai/skills/builtin/commit/SKILL.md +63 -0
  157. connectonion/cli/co_ai/skills/builtin/review-pr/SKILL.md +76 -0
  158. connectonion/cli/co_ai/skills/loader.py +166 -0
  159. connectonion/cli/co_ai/skills/tool.py +46 -0
  160. connectonion/cli/co_ai/tools/__init__.py +92 -0
  161. connectonion/cli/co_ai/tools/ask_user.py +35 -0
  162. connectonion/cli/co_ai/tools/background.py +201 -0
  163. connectonion/cli/co_ai/tools/diff_writer.py +291 -0
  164. connectonion/cli/co_ai/tools/edit.py +89 -0
  165. connectonion/cli/co_ai/tools/glob.py +84 -0
  166. connectonion/cli/co_ai/tools/grep.py +158 -0
  167. connectonion/cli/co_ai/tools/load_guide.py +23 -0
  168. connectonion/cli/co_ai/tools/multi_edit.py +116 -0
  169. connectonion/cli/co_ai/tools/plan_mode.py +169 -0
  170. connectonion/cli/co_ai/tools/read.py +61 -0
  171. connectonion/cli/co_ai/tools/task.py +59 -0
  172. connectonion/cli/co_ai/tools/todo_list.py +159 -0
  173. connectonion/cli/co_ai/tools/write.py +126 -0
  174. connectonion/cli/commands/__init__.py +11 -1
  175. connectonion/cli/commands/ai_commands.py +34 -0
  176. connectonion/cli/commands/copy_commands.py +55 -6
  177. connectonion/cli/commands/create.py +20 -17
  178. connectonion/cli/commands/init.py +19 -22
  179. connectonion/cli/commands/project_cmd_lib.py +15 -0
  180. connectonion/cli/main.py +11 -0
  181. connectonion/console.py +15 -1
  182. connectonion/core/__init__.py +10 -1
  183. connectonion/core/agent.py +37 -16
  184. connectonion/core/exceptions.py +74 -0
  185. connectonion/core/llm.py +54 -6
  186. connectonion/core/tool_executor.py +32 -31
  187. connectonion/core/tool_factory.py +47 -10
  188. connectonion/debug/__init__.py +10 -1
  189. connectonion/debug/debug_explainer/__init__.py +10 -1
  190. connectonion/debug/execution_analyzer/__init__.py +10 -1
  191. connectonion/debug/execution_analyzer/execution_analysis.py +5 -2
  192. connectonion/debug/runtime_inspector/__init__.py +10 -1
  193. connectonion/docs/.package-ignore +6 -0
  194. connectonion/docs/README.md +2036 -0
  195. connectonion/docs/api.md +457 -0
  196. connectonion/docs/archive/001-ai-agent-is-just-prompt-plus-function.md +249 -0
  197. connectonion/docs/archive/README.md +53 -0
  198. connectonion/docs/archive/archive/consolidation-plan.md +72 -0
  199. connectonion/docs/archive/archive/core-principles-extracted.md +239 -0
  200. connectonion/docs/archive/archive/master-principles.md +222 -0
  201. connectonion/docs/archive/archive/principles.md +293 -0
  202. connectonion/docs/archive/archive/simplicity-principles.md +221 -0
  203. connectonion/docs/archive/attack-defense-insights.md +410 -0
  204. connectonion/docs/archive/business-model.md +305 -0
  205. connectonion/docs/archive/core-principles-unified.md +190 -0
  206. connectonion/docs/archive/discussion-journey.md +178 -0
  207. connectonion/docs/archive/economic-analysis.md +323 -0
  208. connectonion/docs/archive/features/01-share-and-find.md +256 -0
  209. connectonion/docs/archive/features/02-agent-authentication.md +93 -0
  210. connectonion/docs/archive/features/03-test-before-trust.md +71 -0
  211. connectonion/docs/archive/features/06-reliability-and-offline.md +197 -0
  212. connectonion/docs/archive/features/README.md +46 -0
  213. connectonion/docs/archive/features-roadmap.md +247 -0
  214. connectonion/docs/archive/mcp-comparison-insights.md +215 -0
  215. connectonion/docs/archive/migration-strategy.md +571 -0
  216. connectonion/docs/archive/mini-whitepaper.md +293 -0
  217. connectonion/docs/archive/network-protocol.md +394 -0
  218. connectonion/docs/archive/semantic-revolution.md +367 -0
  219. connectonion/docs/archive/technical-architecture.md +453 -0
  220. connectonion/docs/archive/the-semantic-insight.md +207 -0
  221. connectonion/docs/archive/threat-model.md +164 -0
  222. connectonion/docs/cli/README.md +805 -0
  223. connectonion/docs/cli/auth.md +46 -0
  224. connectonion/docs/cli/browser.md +235 -0
  225. connectonion/docs/cli/copy.md +232 -0
  226. connectonion/docs/cli/create.md +335 -0
  227. connectonion/docs/cli/init.md +431 -0
  228. connectonion/docs/co-directory-structure.md +214 -0
  229. connectonion/docs/concepts/agent.md +1078 -0
  230. connectonion/docs/concepts/events.md +699 -0
  231. connectonion/docs/concepts/llm_do.md +256 -0
  232. connectonion/docs/concepts/max_iterations.md +362 -0
  233. connectonion/docs/concepts/models.md +641 -0
  234. connectonion/docs/concepts/plugins.md +101 -0
  235. connectonion/docs/concepts/prompts.md +122 -0
  236. connectonion/docs/concepts/session.md +428 -0
  237. connectonion/docs/concepts/tools.md +512 -0
  238. connectonion/docs/concepts/transcribe.md +156 -0
  239. connectonion/docs/concepts/trust.md +291 -0
  240. connectonion/docs/connectonion.md +1256 -0
  241. connectonion/docs/debug/README.md +18 -0
  242. connectonion/docs/debug/auto_debug.md +1026 -0
  243. connectonion/docs/debug/console.md +129 -0
  244. connectonion/docs/debug/eval-format.md +178 -0
  245. connectonion/docs/debug/eval.md +230 -0
  246. connectonion/docs/debug/exceptions.md +307 -0
  247. connectonion/docs/debug/log.md +117 -0
  248. connectonion/docs/debug/xray.md +215 -0
  249. connectonion/docs/design-decisions/001-choosing-input-method.md +202 -0
  250. connectonion/docs/design-decisions/002-choosing-llm-function-name.md +202 -0
  251. connectonion/docs/design-decisions/003-choosing-trust-keyword.md +141 -0
  252. connectonion/docs/design-decisions/004-cli-create-flow.md +117 -0
  253. connectonion/docs/design-decisions/005-designing-agent-network-protocol.md +503 -0
  254. connectonion/docs/design-decisions/006-agent-address-format.md +305 -0
  255. connectonion/docs/design-decisions/007-authentication-backend-design.md +240 -0
  256. connectonion/docs/design-decisions/008-naming-is-hard.md +228 -0
  257. connectonion/docs/design-decisions/009-why-connect-function.md +167 -0
  258. connectonion/docs/design-decisions/010-cli-ux-progressive-disclosure.md +176 -0
  259. connectonion/docs/design-decisions/011-global-config-identity-management.md +357 -0
  260. connectonion/docs/design-decisions/012-tool-execution-separation.md +259 -0
  261. connectonion/docs/design-decisions/013-debug-and-logging-design.md +253 -0
  262. connectonion/docs/design-decisions/014-hook-system-design.md +510 -0
  263. connectonion/docs/design-decisions/015-interactive-auto-debug-design.md +837 -0
  264. connectonion/docs/design-decisions/016-why-no-zero-knowledge-proofs.md +358 -0
  265. connectonion/docs/design-decisions/017-session-logging-and-eval-format.md +120 -0
  266. connectonion/docs/design-decisions/018-event-api-naming.md +274 -0
  267. connectonion/docs/design-decisions/019-agent-lifecycle-design.md +655 -0
  268. connectonion/docs/design-decisions/020-trust-system-and-network-architecture.md +503 -0
  269. connectonion/docs/design-decisions/021-task-storage-jsonl-design.md +496 -0
  270. connectonion/docs/design-decisions/022-raw-asgi-implementation.md +273 -0
  271. connectonion/docs/examples.md +0 -0
  272. connectonion/docs/hook-system-options.md +364 -0
  273. connectonion/docs/integrations/README.md +12 -0
  274. connectonion/docs/integrations/auth.md +450 -0
  275. connectonion/docs/integrations/google.md +431 -0
  276. connectonion/docs/integrations/microsoft.md +370 -0
  277. connectonion/docs/network/README.md +14 -0
  278. connectonion/docs/network/connect.md +629 -0
  279. connectonion/docs/network/deploy.md +124 -0
  280. connectonion/docs/network/host.md +1087 -0
  281. connectonion/docs/network/io.md +538 -0
  282. connectonion/docs/network/protocol/agent-relay-protocol.md +495 -0
  283. connectonion/docs/network/protocol/announce-message.md +115 -0
  284. connectonion/docs/principles.md +124 -0
  285. connectonion/docs/quickstart.md +261 -0
  286. connectonion/docs/roadmap.md +81 -0
  287. connectonion/docs/templates/README.md +77 -0
  288. connectonion/docs/templates/meta-agent.md +152 -0
  289. connectonion/docs/templates/minimal.md +105 -0
  290. connectonion/docs/templates/playwright.md +130 -0
  291. connectonion/docs/templates/web-research.md +144 -0
  292. connectonion/docs/tui/README.md +95 -0
  293. connectonion/docs/tui/chat.md +181 -0
  294. connectonion/docs/tui/divider.md +63 -0
  295. connectonion/docs/tui/dropdown.md +83 -0
  296. connectonion/docs/tui/footer.md +44 -0
  297. connectonion/docs/tui/fuzzy.md +68 -0
  298. connectonion/docs/tui/input.md +84 -0
  299. connectonion/docs/tui/keys.md +77 -0
  300. connectonion/docs/tui/pick.md +71 -0
  301. connectonion/docs/tui/providers.md +89 -0
  302. connectonion/docs/tui/status_bar.md +67 -0
  303. connectonion/docs/useful_plugins/README.md +160 -0
  304. connectonion/docs/useful_plugins/calendar_plugin.md +68 -0
  305. connectonion/docs/useful_plugins/eval.md +89 -0
  306. connectonion/docs/useful_plugins/gmail_plugin.md +68 -0
  307. connectonion/docs/useful_plugins/image_result_formatter.md +74 -0
  308. connectonion/docs/useful_plugins/re_act.md +86 -0
  309. connectonion/docs/useful_plugins/shell_approval.md +69 -0
  310. connectonion/docs/useful_plugins/system_reminder.md +210 -0
  311. connectonion/docs/useful_plugins/tool_approval.md +139 -0
  312. connectonion/docs/useful_prompts/README.md +127 -0
  313. connectonion/docs/useful_prompts/coding_agent.md +214 -0
  314. connectonion/docs/useful_tools/README.md +81 -0
  315. connectonion/docs/useful_tools/ask_user.md +103 -0
  316. connectonion/docs/useful_tools/diff_writer.md +158 -0
  317. connectonion/docs/useful_tools/get_emails.md +519 -0
  318. connectonion/docs/useful_tools/gmail.md +155 -0
  319. connectonion/docs/useful_tools/google_calendar.md +126 -0
  320. connectonion/docs/useful_tools/memory.md +506 -0
  321. connectonion/docs/useful_tools/microsoft_calendar.md +126 -0
  322. connectonion/docs/useful_tools/outlook.md +140 -0
  323. connectonion/docs/useful_tools/send_email.md +423 -0
  324. connectonion/docs/useful_tools/shell.md +115 -0
  325. connectonion/docs/useful_tools/slash_command.md +116 -0
  326. connectonion/docs/useful_tools/terminal.md +115 -0
  327. connectonion/docs/useful_tools/todo_list.md +272 -0
  328. connectonion/docs/useful_tools/web_fetch.md +150 -0
  329. connectonion/docs/vibe-coding-guide.md +97 -0
  330. connectonion/docs/windows-support.md +258 -0
  331. connectonion/logger.py +3 -3
  332. connectonion/network/__init__.py +19 -6
  333. connectonion/network/asgi/__init__.py +81 -0
  334. connectonion/network/asgi/http.py +205 -0
  335. connectonion/network/asgi/websocket.py +217 -0
  336. connectonion/network/connect.py +232 -185
  337. connectonion/network/host/__init__.py +59 -0
  338. connectonion/network/host/auth.py +191 -0
  339. connectonion/network/host/routes.py +135 -0
  340. connectonion/network/host/server.py +289 -0
  341. connectonion/network/host/session.py +78 -0
  342. connectonion/network/io/__init__.py +21 -0
  343. connectonion/network/{connection.py → io/base.py} +17 -42
  344. connectonion/network/io/websocket.py +55 -0
  345. connectonion/network/relay.py +37 -16
  346. connectonion/network/trust/__init__.py +30 -0
  347. connectonion/network/trust/factory.py +138 -0
  348. connectonion/network/{trust_agents.py → trust/prompts.py} +3 -3
  349. connectonion/network/{trust_functions.py → trust/tools.py} +2 -2
  350. connectonion/prompt_files/__init__.py +11 -1
  351. connectonion/prompt_files/react_acknowledge.md +26 -0
  352. connectonion/prompts.py +10 -1
  353. connectonion/tui/chat.py +10 -1
  354. connectonion/tui/divider.py +10 -1
  355. connectonion/tui/dropdown.py +10 -1
  356. connectonion/tui/footer.py +8 -0
  357. connectonion/tui/fuzzy.py +11 -1
  358. connectonion/tui/input.py +118 -70
  359. connectonion/tui/keys.py +133 -6
  360. connectonion/tui/providers.py +11 -1
  361. connectonion/tui/status_bar.py +10 -1
  362. connectonion/useful_events_handlers/__init__.py +8 -0
  363. connectonion/useful_events_handlers/reflect.py +19 -4
  364. connectonion/useful_plugins/__init__.py +3 -1
  365. connectonion/useful_plugins/eval.py +2 -2
  366. connectonion/useful_plugins/gmail_plugin.py +3 -3
  367. connectonion/useful_plugins/image_result_formatter.py +3 -3
  368. connectonion/useful_plugins/re_act.py +114 -28
  369. connectonion/useful_plugins/shell_approval.py +2 -2
  370. connectonion/useful_plugins/system_reminder.py +103 -0
  371. connectonion/useful_plugins/tool_approval.py +233 -0
  372. connectonion/useful_plugins/ui_stream.py +18 -133
  373. connectonion/useful_prompts/README.md +61 -0
  374. connectonion/useful_prompts/__init__.py +45 -0
  375. connectonion/useful_prompts/coding_agent/README.md +106 -0
  376. connectonion/useful_prompts/coding_agent/assembler.py +123 -0
  377. connectonion/useful_prompts/coding_agent/prompts/main.md +227 -0
  378. connectonion/useful_prompts/coding_agent/prompts/tools/ask_user.md +61 -0
  379. connectonion/useful_prompts/coding_agent/prompts/tools/background.md +57 -0
  380. connectonion/useful_prompts/coding_agent/prompts/tools/edit.md +90 -0
  381. connectonion/useful_prompts/coding_agent/prompts/tools/glob.md +52 -0
  382. connectonion/useful_prompts/coding_agent/prompts/tools/grep.md +55 -0
  383. connectonion/useful_prompts/coding_agent/prompts/tools/plan_mode.md +80 -0
  384. connectonion/useful_prompts/coding_agent/prompts/tools/read.md +40 -0
  385. connectonion/useful_prompts/coding_agent/prompts/tools/shell.md +67 -0
  386. connectonion/useful_prompts/coding_agent/prompts/tools/task.md +51 -0
  387. connectonion/useful_prompts/coding_agent/prompts/tools/todo.md +139 -0
  388. connectonion/useful_prompts/coding_agent/prompts/tools/write.md +48 -0
  389. connectonion/useful_prompts/system-reminders/security-warning.md +14 -0
  390. connectonion/useful_prompts/system-reminders/test-reminder.md +11 -0
  391. connectonion/useful_tools/__init__.py +31 -4
  392. connectonion/useful_tools/ask_user.py +35 -0
  393. connectonion/useful_tools/bash.py +69 -0
  394. connectonion/useful_tools/diff_writer.py +186 -94
  395. connectonion/useful_tools/edit.py +102 -0
  396. connectonion/useful_tools/glob_files.py +97 -0
  397. connectonion/useful_tools/grep_files.py +171 -0
  398. connectonion/useful_tools/multi_edit.py +116 -0
  399. connectonion/useful_tools/read_file.py +73 -0
  400. connectonion/useful_tools/shell.py +50 -45
  401. connectonion/useful_tools/write_file.py +129 -0
  402. {connectonion-0.6.2.dist-info → connectonion-0.6.4.dist-info}/METADATA +10 -3
  403. connectonion-0.6.4.dist-info/RECORD +472 -0
  404. connectonion/network/asgi.py +0 -407
  405. connectonion/network/host.py +0 -616
  406. connectonion/network/trust.py +0 -166
  407. connectonion-0.6.2.dist-info/RECORD +0 -129
  408. /connectonion/cli/{docs → co_ai/prompts/connectonion}/connectonion.md +0 -0
  409. {connectonion-0.6.2.dist-info → connectonion-0.6.4.dist-info}/WHEEL +0 -0
  410. {connectonion-0.6.2.dist-info → connectonion-0.6.4.dist-info}/entry_points.txt +0 -0
@@ -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
@@ -0,0 +1,89 @@
1
+ """Edit tool for precise string replacement."""
2
+
3
+ from pathlib import Path
4
+
5
+
6
+ def edit(
7
+ file_path: str,
8
+ old_string: str,
9
+ new_string: str,
10
+ replace_all: bool = False,
11
+ ) -> str:
12
+ """
13
+ Replace a string in a file with precise matching.
14
+
15
+ More token-efficient than rewriting entire files. Use for small, targeted changes.
16
+ The old_string must exist in the file (and be unique unless replace_all=True).
17
+
18
+ Args:
19
+ file_path: Path to the file to edit
20
+ old_string: Exact string to replace (must be unique in file)
21
+ new_string: String to replace with
22
+ replace_all: If True, replace all occurrences; if False, old_string must be unique
23
+
24
+ Returns:
25
+ Success message or error description
26
+
27
+ Examples:
28
+ edit("app.py", "def foo():", "def bar():")
29
+ edit("config.json", '"debug": false', '"debug": true')
30
+ edit("README.md", "v1.0", "v2.0", replace_all=True)
31
+ """
32
+ path = Path(file_path)
33
+
34
+ if not path.exists():
35
+ return f"Error: File '{file_path}' does not exist"
36
+
37
+ if not path.is_file():
38
+ return f"Error: '{file_path}' is not a file"
39
+
40
+ content = path.read_text(encoding="utf-8")
41
+
42
+ # Check if old_string exists
43
+ count = content.count(old_string)
44
+
45
+ if count == 0:
46
+ # Try to help debug: show similar strings
47
+ lines_with_similar = []
48
+ for i, line in enumerate(content.splitlines(), 1):
49
+ # Check if any significant part of old_string is in the line
50
+ if len(old_string) > 10:
51
+ # For longer strings, check first 20 chars
52
+ if old_string[:20] in line or old_string[-20:] in line:
53
+ lines_with_similar.append(f" Line {i}: {line[:80]}")
54
+ elif old_string.strip() in line:
55
+ lines_with_similar.append(f" Line {i}: {line[:80]}")
56
+
57
+ msg = f"Error: String not found in '{file_path}'"
58
+ if lines_with_similar:
59
+ msg += f"\n\nSimilar content found:\n" + "\n".join(lines_with_similar[:5])
60
+ return msg
61
+
62
+ if count > 1 and not replace_all:
63
+ # Show where the duplicates are
64
+ lines_with_match = []
65
+ for i, line in enumerate(content.splitlines(), 1):
66
+ if old_string in line:
67
+ lines_with_match.append(f" Line {i}: {line[:80]}")
68
+
69
+ return (
70
+ f"Error: String appears {count} times in '{file_path}'. "
71
+ f"Use replace_all=True to replace all, or provide more context to make it unique.\n\n"
72
+ f"Found at:\n" + "\n".join(lines_with_match[:10])
73
+ )
74
+
75
+ # Perform replacement
76
+ if replace_all:
77
+ new_content = content.replace(old_string, new_string)
78
+ replaced_count = count
79
+ else:
80
+ new_content = content.replace(old_string, new_string, 1)
81
+ replaced_count = 1
82
+
83
+ # Write back
84
+ path.write_text(new_content, encoding="utf-8")
85
+
86
+ if replace_all and replaced_count > 1:
87
+ return f"Replaced {replaced_count} occurrences in '{file_path}'"
88
+ else:
89
+ return f"Successfully edited '{file_path}'"
@@ -0,0 +1,84 @@
1
+ """Glob tool for file pattern matching."""
2
+
3
+ from pathlib import Path
4
+ from typing import Optional
5
+
6
+ IGNORE_DIRS = {
7
+ ".git",
8
+ "node_modules",
9
+ "__pycache__",
10
+ ".venv",
11
+ "venv",
12
+ ".env",
13
+ "dist",
14
+ "build",
15
+ ".next",
16
+ ".nuxt",
17
+ "target",
18
+ ".idea",
19
+ ".vscode",
20
+ "*.egg-info",
21
+ }
22
+
23
+
24
+ def glob(pattern: str, path: Optional[str] = None) -> str:
25
+ """
26
+ Search for files matching a glob pattern.
27
+
28
+ Args:
29
+ pattern: Glob pattern (e.g., "**/*.py", "src/**/*.ts")
30
+ path: Directory to search in (default: current directory)
31
+
32
+ Returns:
33
+ Matching file paths, one per line, sorted by modification time (newest first)
34
+
35
+ Examples:
36
+ glob("**/*.py") # All Python files
37
+ glob("src/**/*.tsx") # All TSX files in src/
38
+ glob("**/test_*.py") # All test files
39
+ glob("*.md", "docs") # Markdown files in docs/
40
+ """
41
+ base = Path(path) if path else Path.cwd()
42
+
43
+ if not base.exists():
44
+ return f"Error: Path '{base}' does not exist"
45
+
46
+ if not base.is_dir():
47
+ return f"Error: Path '{base}' is not a directory"
48
+
49
+ matches = []
50
+ for p in base.glob(pattern):
51
+ if p.is_file() and not _should_ignore(p):
52
+ matches.append(p)
53
+
54
+ if not matches:
55
+ return f"No files found matching '{pattern}'"
56
+
57
+ # Sort by modification time (newest first)
58
+ matches.sort(key=lambda p: p.stat().st_mtime, reverse=True)
59
+
60
+ # Format output
61
+ results = []
62
+ for p in matches[:100]: # Limit to 100 results
63
+ rel_path = p.relative_to(base) if path else p.relative_to(Path.cwd())
64
+ results.append(str(rel_path))
65
+
66
+ output = "\n".join(results)
67
+
68
+ if len(matches) > 100:
69
+ output += f"\n\n... and {len(matches) - 100} more files"
70
+
71
+ return output
72
+
73
+
74
+ def _should_ignore(path: Path) -> bool:
75
+ """Check if path should be ignored."""
76
+ parts = path.parts
77
+ for part in parts:
78
+ if part in IGNORE_DIRS:
79
+ return True
80
+ # Handle patterns like *.egg-info
81
+ for ignore in IGNORE_DIRS:
82
+ if "*" in ignore and Path(part).match(ignore):
83
+ return True
84
+ return False