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,69 @@
1
+ """
2
+ Bash tool for executing terminal commands (Unix/Mac only).
3
+
4
+ Usage:
5
+ from connectonion import Agent, bash
6
+
7
+ agent = Agent("coder", tools=[bash])
8
+
9
+ # Agent can now use:
10
+ # - bash(command) - Execute bash command, returns output
11
+ # - bash(command, cwd="/path") - Execute in specific directory
12
+ # - bash(command, timeout=60) - Execute with custom timeout
13
+
14
+ Note: This tool is for Unix/Mac systems. For cross-platform usage, use Shell class instead.
15
+ """
16
+
17
+ import subprocess
18
+ import platform
19
+
20
+
21
+ def bash(command: str, cwd: str = ".", timeout: int = 120) -> str:
22
+ """Execute a bash command, returns output (Unix/Mac only).
23
+
24
+ Args:
25
+ command: Bash command to execute (e.g., "ls -la", "git status")
26
+ cwd: Working directory (default: current directory)
27
+ timeout: Seconds before timeout (default: 120, max: 600)
28
+
29
+ Returns:
30
+ Command output (stdout + stderr)
31
+ """
32
+ # Check platform
33
+ if platform.system() == "Windows":
34
+ return "Error: bash tool is for Unix/Mac only. Use Shell class for Windows."
35
+
36
+ # Cap timeout at 10 minutes
37
+ timeout = min(timeout, 600)
38
+
39
+ try:
40
+ result = subprocess.run(
41
+ command,
42
+ shell=True,
43
+ executable="/bin/bash",
44
+ capture_output=True,
45
+ text=True,
46
+ cwd=cwd,
47
+ timeout=timeout
48
+ )
49
+ except subprocess.TimeoutExpired:
50
+ return f"Error: Command timed out after {timeout} seconds"
51
+ except FileNotFoundError:
52
+ return "Error: /bin/bash not found. This tool requires bash shell."
53
+
54
+ parts = []
55
+ if result.stdout:
56
+ parts.append(result.stdout.rstrip())
57
+ if result.stderr:
58
+ parts.append(f"STDERR:\n{result.stderr.rstrip()}")
59
+ if result.returncode != 0:
60
+ parts.append(f"\nExit code: {result.returncode}")
61
+
62
+ output = "\n".join(parts) if parts else "(no output)"
63
+
64
+ # Truncate large outputs
65
+ max_chars = 10000
66
+ if len(output) > max_chars:
67
+ output = output[:max_chars] + f"\n... (truncated, {len(output):,} total chars)"
68
+
69
+ return output
@@ -1,19 +1,21 @@
1
1
  """
2
- Purpose: Human-in-the-loop file writing tool with diff preview and approval workflow
2
+ Purpose: Web-based file writing tool with Claude Code-style permission modes
3
3
  LLM-Note:
4
- Dependencies: imports from [difflib, pathlib, rich.console, rich.panel, rich.text, connectonion.tui.pick] | imported by [useful_tools/__init__.py] | tested by [tests/unit/test_diff_writer.py, tests/unit/test_diff_writer_tool.py]
5
- Data flow: Agent calls DiffWriter.write(path, content) reads existing file → generates unified diff → displays via Rich panel pick() prompts for approval writes if approved → returns status string
6
- State/Effects: reads and writes files on filesystem | displays Rich-formatted diff in terminal | blocks for user input (unless auto_approve=True) | creates new files if path doesn't exist
7
- Integration: exposes DiffWriter class with write(path, content), diff(path, content), read(path) | used as agent tool via Agent(tools=[DiffWriter()]) | auto_approve=True for automation
8
- Performance: file I/O per operation | difflib is O(n) for diff generation | Rich rendering is fast | blocks on user input
9
- Errors: returns error string if file unreadable | returns "Cancelled" if user rejects | no exceptions raised
4
+ Dependencies: imports from [difflib, pathlib, typing] | imported by [useful_tools/__init__.py, __init__.py] | tested by [tests/unit/test_diff_writer.py]
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
10
9
 
11
- DiffWriter - Human-in-the-loop file writing with diff display.
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
12
14
 
13
15
  Usage:
14
16
  from connectonion import Agent, DiffWriter
15
17
 
16
- writer = DiffWriter()
18
+ writer = DiffWriter() # mode="normal" by default
17
19
  agent = Agent("coder", tools=[writer])
18
20
 
19
21
  # Agent can now use:
@@ -24,25 +26,36 @@ Usage:
24
26
 
25
27
  import difflib
26
28
  from pathlib import Path
29
+ from typing import Optional, Tuple
27
30
 
28
- from rich.console import Console
29
- from rich.panel import Panel
30
- from rich.text import Text
31
31
 
32
- from connectonion.tui import pick
32
+ # Permission modes (like Claude Code's Shift+Tab cycle)
33
+ MODE_NORMAL = "normal" # Prompt for every edit
34
+ MODE_AUTO = "auto" # Auto-approve edits
35
+ MODE_PLAN = "plan" # Read-only, no writes
33
36
 
34
37
 
35
38
  class DiffWriter:
36
- """File writer with diff display and human approval."""
39
+ """File writer with Claude Code-style permission modes (web mode).
37
40
 
38
- def __init__(self, auto_approve: bool = False):
41
+ Requires io channel for approval prompts. Without io, falls back to auto-approve.
42
+
43
+ Usage:
44
+ writer = DiffWriter(mode="normal") # Prompt for every edit
45
+ writer = DiffWriter(mode="auto") # Auto-approve all edits
46
+ writer = DiffWriter(mode="plan") # Read-only, preview only
47
+ """
48
+
49
+ def __init__(self, mode: str = MODE_NORMAL, preview_limit: int = 2000):
39
50
  """Initialize DiffWriter.
40
51
 
41
52
  Args:
42
- auto_approve: If True, skip approval prompts (for automation)
53
+ mode: Permission mode - "normal" (prompt), "auto" (auto-approve), "plan" (read-only)
54
+ preview_limit: Max chars to include in diff preview
43
55
  """
44
- self.auto_approve = auto_approve
45
- self._console = Console()
56
+ self.mode = mode
57
+ self.preview_limit = preview_limit
58
+ self.io = None # Set by agent's _sync_tool_io event handler
46
59
 
47
60
  def write(self, path: str, content: str) -> str:
48
61
  """Write content to a file with diff display and approval.
@@ -52,31 +65,42 @@ class DiffWriter:
52
65
  content: Content to write
53
66
 
54
67
  Returns:
55
- Success message, rejection message, or user feedback for agent
68
+ Success message, rejection message with feedback, or plan mode preview
56
69
  """
57
70
  file_path = Path(path)
71
+ file_exists = file_path.exists()
58
72
 
59
- if not self.auto_approve:
73
+ # Plan mode = read-only, just show what would happen
74
+ if self.mode == MODE_PLAN:
60
75
  diff_text = self._generate_diff(path, content)
61
- if diff_text:
62
- self._display_diff(diff_text, path)
63
- else:
64
- self._display_new_file(path, content)
76
+ preview = self._build_preview(diff_text, content, file_exists)
77
+ return f"[Plan mode] Would write {len(content)} bytes to {path}\n\nPreview:\n{preview[:500]}"
78
+
79
+ # Generate diff for display
80
+ diff_text = self._generate_diff(path, content)
81
+ preview = self._build_preview(diff_text, content, file_exists)
82
+ preview, truncated = self._truncate_preview(preview)
65
83
 
66
- choice = self._ask_approval(path)
84
+ # Send diff preview to UI (best-effort, doesn't block)
85
+ self._send_preview(path, preview, truncated, file_exists)
86
+
87
+ # Check approval based on mode
88
+ if self.mode == MODE_NORMAL:
89
+ choice = self._ask_approval(path, preview, truncated)
67
90
 
68
91
  if choice == "reject":
69
- self._console.print()
70
- self._console.print("[bold yellow]What should the agent do instead?[/]")
71
- feedback = input("> ")
92
+ feedback = self._ask_feedback(path)
72
93
  return f"User rejected changes to {path}. Feedback: {feedback}"
73
94
 
74
95
  if choice == "approve_all":
75
- self.auto_approve = True
96
+ self.mode = MODE_AUTO # Switch to auto mode for rest of session
76
97
 
98
+ # Auto mode or approved: write file
77
99
  file_path.parent.mkdir(parents=True, exist_ok=True)
78
- file_path.write_text(content, encoding='utf-8')
79
- return f"Wrote {len(content)} bytes to {path}"
100
+ file_path.write_text(content, encoding="utf-8")
101
+
102
+ mode_note = "" if self.mode == MODE_NORMAL else f" [{self.mode} mode]"
103
+ return f"Wrote {len(content)} bytes to {path}{mode_note}"
80
104
 
81
105
  def diff(self, path: str, content: str) -> str:
82
106
  """Show diff without writing (preview mode).
@@ -90,7 +114,6 @@ class DiffWriter:
90
114
  """
91
115
  diff_text = self._generate_diff(path, content)
92
116
  if diff_text:
93
- self._display_diff(diff_text, path)
94
117
  return diff_text
95
118
  return f"No changes to {path}"
96
119
 
@@ -106,37 +129,126 @@ class DiffWriter:
106
129
  file_path = Path(path)
107
130
  if not file_path.exists():
108
131
  return f"Error: File {path} not found"
109
- return file_path.read_text(encoding='utf-8')
132
+ return file_path.read_text(encoding="utf-8")
133
+
134
+ # =========================================================================
135
+ # IO Communication (Web Mode)
136
+ # =========================================================================
110
137
 
111
- def _ask_approval(self, path: str) -> str:
112
- """Ask user for approval with single keypress.
138
+ def _get_io(self):
139
+ """Return active io channel if available."""
140
+ return self.io
141
+
142
+ def _send_preview(self, path: str, preview: str, truncated: bool, file_exists: bool) -> None:
143
+ """Send diff preview event to UI client.
144
+
145
+ This is informational - UI can render a nice diff view.
146
+ Does not block or wait for response.
147
+ """
148
+ io = self._get_io()
149
+ if not io:
150
+ return
151
+ io.send({
152
+ "type": "diff_preview",
153
+ "path": path,
154
+ "preview": preview,
155
+ "truncated": truncated,
156
+ "file_exists": file_exists,
157
+ })
158
+
159
+ def _ask_approval(self, path: str, preview: str, truncated: bool) -> str:
160
+ """Ask user for approval via io channel.
113
161
 
114
162
  Returns:
115
- 'approve', 'approve_all', or 'reject'
163
+ "approve" - Apply this change
164
+ "approve_all" - Apply and switch to auto mode
165
+ "reject" - Reject and ask for feedback
116
166
  """
117
- self._console.print()
118
- self._console.print(f"[bold cyan]File:[/] {path}")
119
-
120
- choice = pick(
121
- f"Apply changes to {path}?",
122
- {
123
- "1": "Yes, apply this change",
124
- "2": "Yes to all (auto-approve for session)",
125
- "3": "No, and tell agent what to do instead",
126
- },
127
- console=self._console,
167
+ io = self._get_io()
168
+ if not io:
169
+ # No io channel = auto-approve (for non-web usage)
170
+ return "approve"
171
+
172
+ question = f"Apply changes to {path}?"
173
+ if truncated:
174
+ question += " (preview truncated)"
175
+
176
+ response = self._ask_user(
177
+ question,
178
+ options=[
179
+ "Yes, apply this change",
180
+ "Yes to all (auto-approve)",
181
+ "No, reject and give feedback",
182
+ ],
128
183
  )
129
- return {"1": "approve", "2": "approve_all", "3": "reject"}[choice]
184
+
185
+ # Parse response
186
+ if response == "Yes to all (auto-approve)":
187
+ return "approve_all"
188
+ if response == "Yes, apply this change":
189
+ return "approve"
190
+
191
+ # Handle free-text responses
192
+ if response:
193
+ lowered = response.strip().lower()
194
+ if "yes to all" in lowered or lowered == "auto":
195
+ return "approve_all"
196
+ if lowered.startswith("yes") or lowered == "approve":
197
+ return "approve"
198
+
199
+ return "reject"
200
+
201
+ def _ask_feedback(self, path: str) -> str:
202
+ """Ask user for feedback when changes are rejected."""
203
+ feedback = self._ask_user(f"What should the agent do instead for {path}?")
204
+ return feedback or "No feedback provided"
205
+
206
+ def _ask_user(self, question: str, options: Optional[list] = None) -> str:
207
+ """Send ask_user event and block until user responds.
208
+
209
+ Args:
210
+ question: Question to display
211
+ options: List of option strings (buttons in UI)
212
+
213
+ Returns:
214
+ User's answer string
215
+ """
216
+ io = self._get_io()
217
+ if not io:
218
+ return ""
219
+
220
+ # Send ask_user event
221
+ io.send({
222
+ "type": "ask_user",
223
+ "question": question,
224
+ "options": options,
225
+ })
226
+
227
+ # Block waiting for response
228
+ response = io.receive()
229
+
230
+ # Handle connection closed
231
+ if response.get("type") == "io_closed":
232
+ return ""
233
+
234
+ # Extract answer
235
+ answer = response.get("answer", "")
236
+ if isinstance(answer, list):
237
+ return ", ".join([str(a) for a in answer])
238
+ return str(answer) if answer is not None else ""
239
+
240
+ # =========================================================================
241
+ # Diff Generation
242
+ # =========================================================================
130
243
 
131
244
  def _generate_diff(self, path: str, new_content: str) -> str:
132
245
  """Generate unified diff between existing file and new content."""
133
246
  file_path = Path(path)
134
247
 
135
- if file_path.exists():
136
- original_lines = file_path.read_text(encoding='utf-8').splitlines(keepends=True)
137
- else:
138
- return "" # New file, no diff
248
+ if not file_path.exists():
249
+ return "" # New file, no diff to show
139
250
 
251
+ original_lines = file_path.read_text(encoding="utf-8").splitlines(keepends=True)
140
252
  new_lines = new_content.splitlines(keepends=True)
141
253
 
142
254
  diff = difflib.unified_diff(
@@ -146,47 +258,27 @@ class DiffWriter:
146
258
  tofile=f"b/{path}",
147
259
  )
148
260
 
149
- return ''.join(diff)
150
-
151
- def _display_diff(self, diff_text: str, path: str):
152
- """Display colorized diff."""
153
- styled = Text()
154
-
155
- for line in diff_text.splitlines():
156
- if line.startswith('+++') or line.startswith('---'):
157
- styled.append(line + '\n', style="bold white")
158
- elif line.startswith('@@'):
159
- styled.append(line + '\n', style="cyan")
160
- elif line.startswith('+'):
161
- styled.append(line + '\n', style="green")
162
- elif line.startswith('-'):
163
- styled.append(line + '\n', style="red")
164
- else:
165
- styled.append(line + '\n', style="dim")
166
-
167
- panel = Panel(
168
- styled,
169
- title=f"[bold yellow]Changes to {path}[/]",
170
- border_style="yellow",
171
- padding=(0, 1)
172
- )
173
- self._console.print(panel)
174
-
175
- def _display_new_file(self, path: str, content: str):
176
- """Display preview of new file."""
177
- styled = Text()
261
+ return "".join(diff)
178
262
 
263
+ def _build_preview(self, diff_text: str, content: str, file_exists: bool) -> str:
264
+ """Build human-readable preview for approval dialog."""
265
+ if diff_text:
266
+ return diff_text
267
+ if file_exists:
268
+ return "(no changes)"
269
+ return self._new_file_preview(content)
270
+
271
+ def _truncate_preview(self, preview: str) -> Tuple[str, bool]:
272
+ """Truncate preview to configured limit."""
273
+ if len(preview) <= self.preview_limit:
274
+ return preview, False
275
+ return preview[: self.preview_limit] + "\n...(truncated)", True
276
+
277
+ def _new_file_preview(self, content: str, max_lines: int = 50) -> str:
278
+ """Create preview for new file (no existing file to diff against)."""
179
279
  lines = content.splitlines()
180
- for line in lines[:50]: # Limit preview to 50 lines
181
- styled.append(f"+ {line}\n", style="green")
182
-
183
- if len(lines) > 50:
184
- styled.append(f"... ({len(lines) - 50} more lines)\n", style="dim")
185
-
186
- panel = Panel(
187
- styled,
188
- title=f"[bold yellow]New file: {path}[/]",
189
- border_style="yellow",
190
- padding=(0, 1)
191
- )
192
- self._console.print(panel)
280
+ preview_lines = lines[:max_lines]
281
+ preview = "\n".join([f"+ {line}" for line in preview_lines])
282
+ if len(lines) > max_lines:
283
+ preview += f"\n... ({len(lines) - max_lines} more lines)"
284
+ return preview
@@ -0,0 +1,102 @@
1
+ """
2
+ Purpose: Edit tool for precise string replacement (Claude Code-style)
3
+ LLM-Note:
4
+ Dependencies: imports from [pathlib] | imported by [useful_tools/__init__]
5
+ Data flow: Agent calls edit(file_path, old_string, new_string) -> validates -> replaces -> writes
6
+ State/Effects: reads and writes file on filesystem
7
+ Integration: exposes edit(file_path, old_string, new_string) function | used as agent tool
8
+ Errors: returns error if file not found | returns error if old_string not found | returns error if old_string not unique
9
+
10
+ Usage:
11
+ edit("app.py", "def foo():", "def bar():")
12
+ edit("config.json", '"debug": false', '"debug": true')
13
+ edit("README.md", "v1.0", "v2.0", replace_all=True)
14
+ """
15
+
16
+ from pathlib import Path
17
+
18
+
19
+ def edit(
20
+ file_path: str,
21
+ old_string: str,
22
+ new_string: str,
23
+ replace_all: bool = False,
24
+ ) -> str:
25
+ """
26
+ Replace a string in a file with precise matching.
27
+
28
+ More token-efficient than rewriting entire files. Use for small, targeted changes.
29
+ The old_string must exist in the file (and be unique unless replace_all=True).
30
+
31
+ Args:
32
+ file_path: Path to the file to edit
33
+ old_string: Exact string to replace (must be unique in file)
34
+ new_string: String to replace with
35
+ replace_all: If True, replace all occurrences; if False, old_string must be unique
36
+
37
+ Returns:
38
+ Success message or error description
39
+
40
+ Examples:
41
+ edit("app.py", "def foo():", "def bar():")
42
+ edit("config.json", '"debug": false', '"debug": true')
43
+ edit("README.md", "v1.0", "v2.0", replace_all=True)
44
+ """
45
+ path = Path(file_path)
46
+
47
+ if not path.exists():
48
+ return f"Error: File '{file_path}' does not exist"
49
+
50
+ if not path.is_file():
51
+ return f"Error: '{file_path}' is not a file"
52
+
53
+ content = path.read_text(encoding="utf-8")
54
+
55
+ # Check if old_string exists
56
+ count = content.count(old_string)
57
+
58
+ if count == 0:
59
+ # Try to help debug: show similar strings
60
+ lines_with_similar = []
61
+ for i, line in enumerate(content.splitlines(), 1):
62
+ # Check if any significant part of old_string is in the line
63
+ if len(old_string) > 10:
64
+ # For longer strings, check first 20 chars
65
+ if old_string[:20] in line or old_string[-20:] in line:
66
+ lines_with_similar.append(f" Line {i}: {line[:80]}")
67
+ elif old_string.strip() in line:
68
+ lines_with_similar.append(f" Line {i}: {line[:80]}")
69
+
70
+ msg = f"Error: String not found in '{file_path}'"
71
+ if lines_with_similar:
72
+ msg += f"\n\nSimilar content found:\n" + "\n".join(lines_with_similar[:5])
73
+ return msg
74
+
75
+ if count > 1 and not replace_all:
76
+ # Show where the duplicates are
77
+ lines_with_match = []
78
+ for i, line in enumerate(content.splitlines(), 1):
79
+ if old_string in line:
80
+ lines_with_match.append(f" Line {i}: {line[:80]}")
81
+
82
+ return (
83
+ f"Error: String appears {count} times in '{file_path}'. "
84
+ f"Use replace_all=True to replace all, or provide more context to make it unique.\n\n"
85
+ f"Found at:\n" + "\n".join(lines_with_match[:10])
86
+ )
87
+
88
+ # Perform replacement
89
+ if replace_all:
90
+ new_content = content.replace(old_string, new_string)
91
+ replaced_count = count
92
+ else:
93
+ new_content = content.replace(old_string, new_string, 1)
94
+ replaced_count = 1
95
+
96
+ # Write back
97
+ path.write_text(new_content, encoding="utf-8")
98
+
99
+ if replace_all and replaced_count > 1:
100
+ return f"Replaced {replaced_count} occurrences in '{file_path}'"
101
+ else:
102
+ return f"Successfully edited '{file_path}'"
@@ -0,0 +1,97 @@
1
+ """
2
+ Purpose: Glob tool for file pattern matching
3
+ LLM-Note:
4
+ Dependencies: imports from [pathlib, typing] | imported by [useful_tools/__init__, grep]
5
+ Data flow: Agent calls glob(pattern) -> searches directory -> returns matching paths
6
+ State/Effects: reads filesystem (no writes)
7
+ Integration: exposes glob(pattern, path) function | used as agent tool | shared IGNORE_DIRS constant
8
+
9
+ Usage:
10
+ glob("**/*.py") # All Python files
11
+ glob("src/**/*.tsx") # All TSX files in src/
12
+ glob("**/test_*.py") # All test files
13
+ glob("*.md", "docs") # Markdown files in docs/
14
+ """
15
+
16
+ from pathlib import Path
17
+ from typing import Optional
18
+
19
+ IGNORE_DIRS = {
20
+ ".git",
21
+ "node_modules",
22
+ "__pycache__",
23
+ ".venv",
24
+ "venv",
25
+ ".env",
26
+ "dist",
27
+ "build",
28
+ ".next",
29
+ ".nuxt",
30
+ "target",
31
+ ".idea",
32
+ ".vscode",
33
+ "*.egg-info",
34
+ }
35
+
36
+
37
+ def glob(pattern: str, path: Optional[str] = None) -> str:
38
+ """
39
+ Search for files matching a glob pattern.
40
+
41
+ Args:
42
+ pattern: Glob pattern (e.g., "**/*.py", "src/**/*.ts")
43
+ path: Directory to search in (default: current directory)
44
+
45
+ Returns:
46
+ Matching file paths, one per line, sorted by modification time (newest first)
47
+
48
+ Examples:
49
+ glob("**/*.py") # All Python files
50
+ glob("src/**/*.tsx") # All TSX files in src/
51
+ glob("**/test_*.py") # All test files
52
+ glob("*.md", "docs") # Markdown files in docs/
53
+ """
54
+ base = Path(path) if path else Path.cwd()
55
+
56
+ if not base.exists():
57
+ return f"Error: Path '{base}' does not exist"
58
+
59
+ if not base.is_dir():
60
+ return f"Error: Path '{base}' is not a directory"
61
+
62
+ matches = []
63
+ for p in base.glob(pattern):
64
+ if p.is_file() and not _should_ignore(p):
65
+ matches.append(p)
66
+
67
+ if not matches:
68
+ return f"No files found matching '{pattern}'"
69
+
70
+ # Sort by modification time (newest first)
71
+ matches.sort(key=lambda p: p.stat().st_mtime, reverse=True)
72
+
73
+ # Format output
74
+ results = []
75
+ for p in matches[:100]: # Limit to 100 results
76
+ rel_path = p.relative_to(base) if path else p.relative_to(Path.cwd())
77
+ results.append(str(rel_path))
78
+
79
+ output = "\n".join(results)
80
+
81
+ if len(matches) > 100:
82
+ output += f"\n\n... and {len(matches) - 100} more files"
83
+
84
+ return output
85
+
86
+
87
+ def _should_ignore(path: Path) -> bool:
88
+ """Check if path should be ignored."""
89
+ parts = path.parts
90
+ for part in parts:
91
+ if part in IGNORE_DIRS:
92
+ return True
93
+ # Handle patterns like *.egg-info
94
+ for ignore in IGNORE_DIRS:
95
+ if "*" in ignore and Path(part).match(ignore):
96
+ return True
97
+ return False