oto-cli 1.0.0__tar.gz

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 (235) hide show
  1. oto_cli-1.0.0/.claude/settings.local.json +20 -0
  2. oto_cli-1.0.0/.gitignore +13 -0
  3. oto_cli-1.0.0/CLAUDE.md +117 -0
  4. oto_cli-1.0.0/LICENSE +21 -0
  5. oto_cli-1.0.0/PKG-INFO +171 -0
  6. oto_cli-1.0.0/README.md +131 -0
  7. oto_cli-1.0.0/TODO.md +45 -0
  8. oto_cli-1.0.0/docs/concepts.md +136 -0
  9. oto_cli-1.0.0/docs/create-connector.md +174 -0
  10. oto_cli-1.0.0/docs/gmail-oauth-setup.md +125 -0
  11. oto_cli-1.0.0/docs/google-service-account-setup.md +154 -0
  12. oto_cli-1.0.0/docs/installation.md +134 -0
  13. oto_cli-1.0.0/oto/__init__.py +3 -0
  14. oto_cli-1.0.0/oto/cli.py +74 -0
  15. oto_cli-1.0.0/oto/commands/__init__.py +0 -0
  16. oto_cli-1.0.0/oto/commands/anthropic.py +64 -0
  17. oto_cli-1.0.0/oto/commands/audio.py +91 -0
  18. oto_cli-1.0.0/oto/commands/browser.py +257 -0
  19. oto_cli-1.0.0/oto/commands/company.py +23 -0
  20. oto_cli-1.0.0/oto/commands/enrichment.py +152 -0
  21. oto_cli-1.0.0/oto/commands/folk.py +238 -0
  22. oto_cli-1.0.0/oto/commands/google.py +355 -0
  23. oto_cli-1.0.0/oto/commands/notion.py +58 -0
  24. oto_cli-1.0.0/oto/commands/pennylane.py +272 -0
  25. oto_cli-1.0.0/oto/commands/search.py +36 -0
  26. oto_cli-1.0.0/oto/commands/sirene.py +201 -0
  27. oto_cli-1.0.0/oto/commands/skills.py +91 -0
  28. oto_cli-1.0.0/oto/commands/whatsapp.py +47 -0
  29. oto_cli-1.0.0/oto/config.py +167 -0
  30. oto_cli-1.0.0/oto/tools/.cache/notion/07a366cf2d7a0f77e7a1736805e94786e6053bb581b6b7179591b71d34843ce8.json +98 -0
  31. oto_cli-1.0.0/oto/tools/.cache/notion/0b21878dbd4bf5a67ff52104361a07ee575b03a075b7b81cbc4a0d5d4045f759.json +142 -0
  32. oto_cli-1.0.0/oto/tools/.cache/notion/0bf47e753ec29da03776bca010122393b4fc188af7f28dd11b456a74e5cae8f4.json +98 -0
  33. oto_cli-1.0.0/oto/tools/.cache/notion/1b2c924688fdcef8b7e3748f92ed4fdb232cadb387a7e9e157c438d32374ff97.json +4356 -0
  34. oto_cli-1.0.0/oto/tools/.cache/notion/2a66512043d3531dc900b8244fc13972c0abfe7760c10fbb7fc21f309238ad0b.json +132 -0
  35. oto_cli-1.0.0/oto/tools/.cache/notion/2b59aed15d0c747ec2cae70a1eb3f554b3e170875110b82f8e4614b761c84dcf.json +142 -0
  36. oto_cli-1.0.0/oto/tools/.cache/notion/2f7d1284021d30e842cc9a2b506e87b4a32c61e8e9abb616aec37828c441231d.json +220 -0
  37. oto_cli-1.0.0/oto/tools/.cache/notion/33eb548bce70fb7b0e8eb09211ba6c03ad22b6e7a289606b05c5d690f724d8e4.json +1659 -0
  38. oto_cli-1.0.0/oto/tools/.cache/notion/364d3a5e790f3d8b5137aae0a5a9ee151cd8c9f7dacb4fd669220f968405ea36.json +88 -0
  39. oto_cli-1.0.0/oto/tools/.cache/notion/3e7e836a6bf18f951d4887fefeacb146473491ad2cbaa1793c79af08b7ef45b6.json +132 -0
  40. oto_cli-1.0.0/oto/tools/.cache/notion/3f95b2d8bdf3d47e8f6c167d6d9a0889f27b64e8c78f0afdad538fecae070858.json +98 -0
  41. oto_cli-1.0.0/oto/tools/.cache/notion/46c38d10065fb68ca912692df53b6e54eb1f6f9cdff342de563020917e27ccf5.json +98 -0
  42. oto_cli-1.0.0/oto/tools/.cache/notion/51fa4d44044d751ca2319d87d607bd58a01004a24a8065808d65c273b35d3a10.json +210 -0
  43. oto_cli-1.0.0/oto/tools/.cache/notion/59888e9c297cda6836afb560fb4048b100f98eb3abead8b175fe92fbf858210b.json +186 -0
  44. oto_cli-1.0.0/oto/tools/.cache/notion/60df8d17a4e08fdbafb0ea398ff68d45cb79da7c01bd129e98264be1bff7ed1d.json +176 -0
  45. oto_cli-1.0.0/oto/tools/.cache/notion/62a44af3f86b5841d905789565a0208f80c1a304bcfc8889ccd55f61f29508b2.json +132 -0
  46. oto_cli-1.0.0/oto/tools/.cache/notion/69ec094fc12a40d1591abbd4f21295af672389963f900ea421a6d75b6f21f03b.json +176 -0
  47. oto_cli-1.0.0/oto/tools/.cache/notion/703fc49f07e73447e176fa69f31c7ff7bbb7c8c95325af4ca1a8664385bfeb0b.json +98 -0
  48. oto_cli-1.0.0/oto/tools/.cache/notion/7e247cf818c2a930de8a5a8599716900cb5af90570a9f701699dd8897fe43c8d.json +88 -0
  49. oto_cli-1.0.0/oto/tools/.cache/notion/86741d020ca89789bda8156315ead5e980068c19754baf6e611ffc618557dc06.json +115 -0
  50. oto_cli-1.0.0/oto/tools/.cache/notion/8cc6e5e1d955310412df09c9179ec67d42767d1b557262e28bc2afea05ab7c24.json +210 -0
  51. oto_cli-1.0.0/oto/tools/.cache/notion/989d88c22dd009e5683987e70671d1e20fd6a5a3e9ee7670db9ea602fe8fbded.json +4605 -0
  52. oto_cli-1.0.0/oto/tools/.cache/notion/999d01d80c8ef0c856572028e31e701cad8c5704b8886388b756d32ef50cbfaf.json +132 -0
  53. oto_cli-1.0.0/oto/tools/.cache/notion/9f10e5fa6404211b156b2f5c22a0644e5eb0c49774d2b4f8d768211f0c6aea22.json +3380 -0
  54. oto_cli-1.0.0/oto/tools/.cache/notion/9f950bdfc2aeb9ae5e1b185e827636f83fa44a9d7de0d1462d87926508b26907.json +142 -0
  55. oto_cli-1.0.0/oto/tools/.cache/notion/a4719c76c9ff4c4b056e87689e2f487770c35d82d70acb098ead7712e0d3be8d.json +142 -0
  56. oto_cli-1.0.0/oto/tools/.cache/notion/a808160a7352fa1a3029ef1e2929dfa99b5021b0d22ce1cfb9efe3c68f7e7af1.json +98 -0
  57. oto_cli-1.0.0/oto/tools/.cache/notion/a9c29806556409fbfcd7622b876244caed085c63c70380dc6caacd162e6d05a9.json +132 -0
  58. oto_cli-1.0.0/oto/tools/.cache/notion/b3142c5509452a15382cade5cb81324a26d1946d128a1b4342a143872f9354fe.json +149 -0
  59. oto_cli-1.0.0/oto/tools/.cache/notion/b516a0820a66ae04229bfcb7f0463be0867f31a8dc1b875e57aba02b970a7a31.json +98 -0
  60. oto_cli-1.0.0/oto/tools/.cache/notion/c175f8c35c81ebd2fb9e4bf4d2f1cc29f98b45006f59354c9f33ee780609d896.json +247 -0
  61. oto_cli-1.0.0/oto/tools/.cache/notion/c950254390b901d475937c868e27e74960720fe65e2d8506bd39e30c634e619d.json +98 -0
  62. oto_cli-1.0.0/oto/tools/.cache/notion/cac99fb54fa178e1a7f72e5212e1e99eff5fe72a1fa52f65fcee432bcf528b53.json +166 -0
  63. oto_cli-1.0.0/oto/tools/.cache/notion/e38237abecf19ce5b9a92ed90723d2642a11b0f1ef189b1a1fc10b7ebcd3d4a4.json +88 -0
  64. oto_cli-1.0.0/oto/tools/.cache/notion/e58e933def9b7000d791c83f6660adbecac59ab61e54f8d855a52d80200eb929.json +292 -0
  65. oto_cli-1.0.0/oto/tools/.cache/notion/f8088377a1049850b0ec1acabf09da8504fb1f194479cc43ce4c31db3895afcd.json +176 -0
  66. oto_cli-1.0.0/oto/tools/.cache/notion/fc2ccb6edbe2020010e036a69e7ece3ab3f007c9a4fed39066d6792d332e65af.json +142 -0
  67. oto_cli-1.0.0/oto/tools/.cache/notion/ff117c74176f8fc26aba7f06649c7794dc4a0c51194033f453b16c8af8ac4201.json +36 -0
  68. oto_cli-1.0.0/oto/tools/__init__.py +1 -0
  69. oto_cli-1.0.0/oto/tools/anthropic/__init__.py +5 -0
  70. oto_cli-1.0.0/oto/tools/anthropic/client.py +354 -0
  71. oto_cli-1.0.0/oto/tools/anthropic_batch/__init__.py +5 -0
  72. oto_cli-1.0.0/oto/tools/anthropic_batch/client.py +352 -0
  73. oto_cli-1.0.0/oto/tools/apollo/__init__.py +5 -0
  74. oto_cli-1.0.0/oto/tools/apollo/client.py +191 -0
  75. oto_cli-1.0.0/oto/tools/attio/__init__.py +5 -0
  76. oto_cli-1.0.0/oto/tools/attio/client.py +242 -0
  77. oto_cli-1.0.0/oto/tools/audio/__init__.py +5 -0
  78. oto_cli-1.0.0/oto/tools/audio/client.py +116 -0
  79. oto_cli-1.0.0/oto/tools/browser/__init__.py +26 -0
  80. oto_cli-1.0.0/oto/tools/browser/crunchbase.py +423 -0
  81. oto_cli-1.0.0/oto/tools/browser/g2.py +236 -0
  82. oto_cli-1.0.0/oto/tools/browser/indeed.py +282 -0
  83. oto_cli-1.0.0/oto/tools/browser/linkedin.py +821 -0
  84. oto_cli-1.0.0/oto/tools/browser/pappers.py +344 -0
  85. oto_cli-1.0.0/oto/tools/clearbit/__init__.py +5 -0
  86. oto_cli-1.0.0/oto/tools/clearbit/client.py +126 -0
  87. oto_cli-1.0.0/oto/tools/collective/__init__.py +5 -0
  88. oto_cli-1.0.0/oto/tools/collective/client.py +333 -0
  89. oto_cli-1.0.0/oto/tools/common/__init__.py +5 -0
  90. oto_cli-1.0.0/oto/tools/common/rate_limiter.py +457 -0
  91. oto_cli-1.0.0/oto/tools/figma/__init__.py +5 -0
  92. oto_cli-1.0.0/oto/tools/figma/client.py +254 -0
  93. oto_cli-1.0.0/oto/tools/folk/__init__.py +5 -0
  94. oto_cli-1.0.0/oto/tools/folk/client.py +203 -0
  95. oto_cli-1.0.0/oto/tools/gemini/__init__.py +5 -0
  96. oto_cli-1.0.0/oto/tools/gemini/client.py +264 -0
  97. oto_cli-1.0.0/oto/tools/google/__init__.py +1 -0
  98. oto_cli-1.0.0/oto/tools/google/calendar/__init__.py +0 -0
  99. oto_cli-1.0.0/oto/tools/google/calendar/lib/__init__.py +0 -0
  100. oto_cli-1.0.0/oto/tools/google/calendar/lib/calendar_client.py +155 -0
  101. oto_cli-1.0.0/oto/tools/google/credentials.py +177 -0
  102. oto_cli-1.0.0/oto/tools/google/docs/get_section.py +53 -0
  103. oto_cli-1.0.0/oto/tools/google/docs/insert_section.py +56 -0
  104. oto_cli-1.0.0/oto/tools/google/docs/insert_text.py +99 -0
  105. oto_cli-1.0.0/oto/tools/google/docs/lib/__init__.py +0 -0
  106. oto_cli-1.0.0/oto/tools/google/docs/lib/docs_client.py +341 -0
  107. oto_cli-1.0.0/oto/tools/google/docs/list_headings.py +44 -0
  108. oto_cli-1.0.0/oto/tools/google/docs/move_section.py +37 -0
  109. oto_cli-1.0.0/oto/tools/google/docs/replace_section.py +47 -0
  110. oto_cli-1.0.0/oto/tools/google/docs/sync/__init__.py +1 -0
  111. oto_cli-1.0.0/oto/tools/google/docs/sync/gdoc_sync.py +427 -0
  112. oto_cli-1.0.0/oto/tools/google/drive/README.md +308 -0
  113. oto_cli-1.0.0/oto/tools/google/drive/check_quota.py +31 -0
  114. oto_cli-1.0.0/oto/tools/google/drive/copy_file.py +76 -0
  115. oto_cli-1.0.0/oto/tools/google/drive/create_folder.py +58 -0
  116. oto_cli-1.0.0/oto/tools/google/drive/download_file.py +60 -0
  117. oto_cli-1.0.0/oto/tools/google/drive/export_doc.py +64 -0
  118. oto_cli-1.0.0/oto/tools/google/drive/export_slides.py +95 -0
  119. oto_cli-1.0.0/oto/tools/google/drive/extract_slides_content.py +113 -0
  120. oto_cli-1.0.0/oto/tools/google/drive/lib/drive_client.py +454 -0
  121. oto_cli-1.0.0/oto/tools/google/drive/list_files.py +97 -0
  122. oto_cli-1.0.0/oto/tools/google/drive/list_shared_drives.py +32 -0
  123. oto_cli-1.0.0/oto/tools/google/drive/move_file.py +57 -0
  124. oto_cli-1.0.0/oto/tools/google/drive/requirements.txt +4 -0
  125. oto_cli-1.0.0/oto/tools/google/drive/tool.yaml +210 -0
  126. oto_cli-1.0.0/oto/tools/google/drive/upload_file.py +90 -0
  127. oto_cli-1.0.0/oto/tools/google/gmail/__init__.py +1 -0
  128. oto_cli-1.0.0/oto/tools/google/gmail/get_message.py +33 -0
  129. oto_cli-1.0.0/oto/tools/google/gmail/lib/__init__.py +1 -0
  130. oto_cli-1.0.0/oto/tools/google/gmail/lib/gmail_client.py +385 -0
  131. oto_cli-1.0.0/oto/tools/google/gmail/list_messages.py +37 -0
  132. oto_cli-1.0.0/oto/tools/google/gmail/requirements.txt +3 -0
  133. oto_cli-1.0.0/oto/tools/google/gmail/search.py +34 -0
  134. oto_cli-1.0.0/oto/tools/google/gmail/send.py +39 -0
  135. oto_cli-1.0.0/oto/tools/google/gmail/tool.yaml +107 -0
  136. oto_cli-1.0.0/oto/tools/google/keep/__init__.py +0 -0
  137. oto_cli-1.0.0/oto/tools/google/keep/create_note.py +42 -0
  138. oto_cli-1.0.0/oto/tools/google/keep/get_master_token.py +101 -0
  139. oto_cli-1.0.0/oto/tools/google/keep/get_note.py +33 -0
  140. oto_cli-1.0.0/oto/tools/google/keep/lib/__init__.py +0 -0
  141. oto_cli-1.0.0/oto/tools/google/keep/lib/keep_client.py +273 -0
  142. oto_cli-1.0.0/oto/tools/google/keep/list_notes.py +42 -0
  143. oto_cli-1.0.0/oto/tools/google/keep/search.py +34 -0
  144. oto_cli-1.0.0/oto/tools/google/sheets/create_sheet_direct.py +157 -0
  145. oto_cli-1.0.0/oto/tools/google/sheets/create_sheet_from_csv.py +167 -0
  146. oto_cli-1.0.0/oto/tools/google/sheets/create_sheet_in_folder.py +165 -0
  147. oto_cli-1.0.0/oto/tools/google/sheets/create_sheet_via_drive.py +157 -0
  148. oto_cli-1.0.0/oto/tools/google/sheets/tool.yaml +74 -0
  149. oto_cli-1.0.0/oto/tools/google/slides/.folders +19 -0
  150. oto_cli-1.0.0/oto/tools/google/slides/321-LAYOUTS.md +203 -0
  151. oto_cli-1.0.0/oto/tools/google/slides/README.md +210 -0
  152. oto_cli-1.0.0/oto/tools/google/slides/USAGE-EDIT.md +626 -0
  153. oto_cli-1.0.0/oto/tools/google/slides/USAGE.md +158 -0
  154. oto_cli-1.0.0/oto/tools/google/slides/create-demo-presentation.py +348 -0
  155. oto_cli-1.0.0/oto/tools/google/slides/generate_slides.py +437 -0
  156. oto_cli-1.0.0/oto/tools/google/slides/lib/__init__.py +1 -0
  157. oto_cli-1.0.0/oto/tools/google/slides/lib/content_filler.py +579 -0
  158. oto_cli-1.0.0/oto/tools/google/slides/lib/layout_mappings.py +182 -0
  159. oto_cli-1.0.0/oto/tools/google/slides/lib/slides_client.py +1239 -0
  160. oto_cli-1.0.0/oto/tools/google/slides/requirements.txt +5 -0
  161. oto_cli-1.0.0/oto/tools/google/slides/test-copy-and-edit.py +187 -0
  162. oto_cli-1.0.0/oto/tools/google/slides/test-copy-slide.py +148 -0
  163. oto_cli-1.0.0/oto/tools/google/slides/test-edit.py +183 -0
  164. oto_cli-1.0.0/oto/tools/google/slides/tool.yaml +37 -0
  165. oto_cli-1.0.0/oto/tools/groq/__init__.py +5 -0
  166. oto_cli-1.0.0/oto/tools/groq/client.py +165 -0
  167. oto_cli-1.0.0/oto/tools/hithorizons/__init__.py +5 -0
  168. oto_cli-1.0.0/oto/tools/hithorizons/client.py +168 -0
  169. oto_cli-1.0.0/oto/tools/hunter/__init__.py +5 -0
  170. oto_cli-1.0.0/oto/tools/hunter/client.py +104 -0
  171. oto_cli-1.0.0/oto/tools/kaspr/__init__.py +5 -0
  172. oto_cli-1.0.0/oto/tools/kaspr/client.py +80 -0
  173. oto_cli-1.0.0/oto/tools/lemlist/__init__.py +5 -0
  174. oto_cli-1.0.0/oto/tools/lemlist/client.py +486 -0
  175. oto_cli-1.0.0/oto/tools/mistral/__init__.py +5 -0
  176. oto_cli-1.0.0/oto/tools/mistral/client.py +149 -0
  177. oto_cli-1.0.0/oto/tools/naf/__init__.py +5 -0
  178. oto_cli-1.0.0/oto/tools/naf/suggester.py +140 -0
  179. oto_cli-1.0.0/oto/tools/notion/README.md +310 -0
  180. oto_cli-1.0.0/oto/tools/notion/append_blocks.py +204 -0
  181. oto_cli-1.0.0/oto/tools/notion/append_blocks_chunked.py +296 -0
  182. oto_cli-1.0.0/oto/tools/notion/count_database_entries.py +47 -0
  183. oto_cli-1.0.0/oto/tools/notion/create_database_from_csv.py +269 -0
  184. oto_cli-1.0.0/oto/tools/notion/create_page.py +65 -0
  185. oto_cli-1.0.0/oto/tools/notion/get_database.py +67 -0
  186. oto_cli-1.0.0/oto/tools/notion/get_page.py +78 -0
  187. oto_cli-1.0.0/oto/tools/notion/known-pages.md +75 -0
  188. oto_cli-1.0.0/oto/tools/notion/lib/markdown_converter.py +287 -0
  189. oto_cli-1.0.0/oto/tools/notion/lib/notion_client.py +313 -0
  190. oto_cli-1.0.0/oto/tools/notion/list_teamspaces.py +244 -0
  191. oto_cli-1.0.0/oto/tools/notion/notion +122 -0
  192. oto_cli-1.0.0/oto/tools/notion/query_database.py +77 -0
  193. oto_cli-1.0.0/oto/tools/notion/requirements.txt +1 -0
  194. oto_cli-1.0.0/oto/tools/notion/search.py +93 -0
  195. oto_cli-1.0.0/oto/tools/notion/tool.yaml +235 -0
  196. oto_cli-1.0.0/oto/tools/notion/update_page.py +65 -0
  197. oto_cli-1.0.0/oto/tools/pennylane/__init__.py +5 -0
  198. oto_cli-1.0.0/oto/tools/pennylane/client.py +386 -0
  199. oto_cli-1.0.0/oto/tools/phantombuster/__init__.py +5 -0
  200. oto_cli-1.0.0/oto/tools/phantombuster/client.py +177 -0
  201. oto_cli-1.0.0/oto/tools/resend/__init__.py +5 -0
  202. oto_cli-1.0.0/oto/tools/resend/client.py +149 -0
  203. oto_cli-1.0.0/oto/tools/serpapi/__init__.py +5 -0
  204. oto_cli-1.0.0/oto/tools/serpapi/client.py +128 -0
  205. oto_cli-1.0.0/oto/tools/serper/__init__.py +5 -0
  206. oto_cli-1.0.0/oto/tools/serper/client.py +210 -0
  207. oto_cli-1.0.0/oto/tools/sirene/__init__.py +34 -0
  208. oto_cli-1.0.0/oto/tools/sirene/client.py +474 -0
  209. oto_cli-1.0.0/oto/tools/sirene/data/naf_codes.txt +732 -0
  210. oto_cli-1.0.0/oto/tools/sirene/entreprises.py +175 -0
  211. oto_cli-1.0.0/oto/tools/sirene/stock.py +377 -0
  212. oto_cli-1.0.0/oto/tools/slack/__init__.py +5 -0
  213. oto_cli-1.0.0/oto/tools/slack/client.py +202 -0
  214. oto_cli-1.0.0/oto/tools/unsplash/__init__.py +5 -0
  215. oto_cli-1.0.0/oto/tools/unsplash/client.py +197 -0
  216. oto_cli-1.0.0/oto/tools/whatsapp/__init__.py +3 -0
  217. oto_cli-1.0.0/oto/tools/whatsapp/client.py +68 -0
  218. oto_cli-1.0.0/oto/tools/whatsapp/node/package-lock.json +1564 -0
  219. oto_cli-1.0.0/oto/tools/whatsapp/node/package.json +11 -0
  220. oto_cli-1.0.0/oto/tools/whatsapp/node/whatsapp.mjs +357 -0
  221. oto_cli-1.0.0/oto/tools/wttj/__init__.py +5 -0
  222. oto_cli-1.0.0/oto/tools/wttj/client.py +251 -0
  223. oto_cli-1.0.0/oto/tools/zerobounce/__init__.py +5 -0
  224. oto_cli-1.0.0/oto/tools/zerobounce/client.py +93 -0
  225. oto_cli-1.0.0/pyproject.toml +59 -0
  226. oto_cli-1.0.0/skills/oto-anthropic/SKILL.md +38 -0
  227. oto_cli-1.0.0/skills/oto-browser/SKILL.md +103 -0
  228. oto_cli-1.0.0/skills/oto-cli/SKILL.md +100 -0
  229. oto_cli-1.0.0/skills/oto-enrichment/SKILL.md +60 -0
  230. oto_cli-1.0.0/skills/oto-google/SKILL.md +83 -0
  231. oto_cli-1.0.0/skills/oto-notion/SKILL.md +35 -0
  232. oto_cli-1.0.0/skills/oto-pennylane/SKILL.md +42 -0
  233. oto_cli-1.0.0/skills/oto-search/SKILL.md +29 -0
  234. oto_cli-1.0.0/skills/oto-sirene/SKILL.md +75 -0
  235. oto_cli-1.0.0/skills/oto-whatsapp/SKILL.md +33 -0
@@ -0,0 +1,20 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(mkdir:*)",
5
+ "Bash(source:*)",
6
+ "Bash(node:*)",
7
+ "Bash(oto whatsapp:*)",
8
+ "Bash(oto google:*)",
9
+ "Skill(claude-api)",
10
+ "Bash(npm list:*)",
11
+ "Bash(twilio:*)",
12
+ "Bash(ngrok:*)",
13
+ "Bash(sqlite3:*)",
14
+ "Bash(oto browser:*)",
15
+ "Bash(oto:*)",
16
+ "Bash(pipx list:*)",
17
+ "Bash(hatch build:*)"
18
+ ]
19
+ }
20
+ }
@@ -0,0 +1,13 @@
1
+ venv/
2
+ __pycache__/
3
+ *.egg-info/
4
+ dist/
5
+ oto/tools/whatsapp/node/node_modules/
6
+
7
+ # Secrets
8
+ .otomata/
9
+ secrets.env
10
+ *-token-*.json
11
+
12
+ # Internal
13
+ PLAN.md
@@ -0,0 +1,117 @@
1
+ # Oto
2
+
3
+ CLI toolkit for AI agents — covers the long tail of SaaS APIs that don't have a CLI.
4
+
5
+ Repo: `AlexisLaporte/oto`. Package: `oto-cli` on PyPI. Command: `oto`.
6
+
7
+ ## Philosophy
8
+
9
+ - **CLI-first**: everything goes through `oto <command>`, no MCP, no server
10
+ - **For AI agents**: JSON on stdout, errors on stderr, composable with pipes
11
+ - **Modular**: each connector is a separate file, auto-discovered at startup
12
+ - **No over-engineering**: no plugin registry, no ABC, no MCP
13
+
14
+ ## Stack
15
+
16
+ - Python 3.10+, Typer (CLI), Hatchling (build)
17
+ - Google APIs (auth, drive, docs, sheets, slides, gmail, keep)
18
+ - o-browser (browser automation, Patchright) — optional
19
+ - Requests (HTTP), python-dotenv (secrets)
20
+
21
+ ## Architecture
22
+
23
+ ```
24
+ oto/
25
+ ├── oto/
26
+ │ ├── cli.py # Dynamic command discovery + main()
27
+ │ ├── config.py # Secrets 3-tier (.otomata/secrets.env)
28
+ │ ├── commands/ # 1 file = 1 sub-command (auto-discovered)
29
+ │ │ ├── google.py # drive, docs, sheets, slides, gmail, calendar, auth
30
+ │ │ ├── notion.py # search, page, database
31
+ │ │ ├── browser.py # linkedin, crunchbase, pappers, indeed, g2
32
+ │ │ ├── sirene.py # SIRENE API (search, get, stock)
33
+ │ │ ├── search.py # web, news (serper)
34
+ │ │ ├── enrichment.py # kaspr, hunter, lemlist
35
+ │ │ ├── pennylane.py # accounting
36
+ │ │ ├── anthropic.py # usage, cost, summary
37
+ │ │ ├── company.py # SIREN lookup multi-source
38
+ │ │ ├── whatsapp.py # WhatsApp messaging
39
+ │ │ └── skills.py # Claude Code skills (enable/disable)
40
+ │ └── tools/ # API clients
41
+ │ ├── google/ # gmail, drive, docs, sheets, slides, calendar, keep
42
+ │ ├── notion/ # pages, databases, search
43
+ │ ├── browser/ # linkedin, crunchbase, pappers, indeed, g2
44
+ │ ├── whatsapp/ # Node.js bridge (whatsapp-web.js)
45
+ │ ├── sirene/ # INSEE SIRENE API
46
+ │ ├── serper/ # Google search (web, news)
47
+ │ ├── anthropic/ # Admin API (usage, costs)
48
+ │ ├── pennylane/ # Accounting
49
+ │ ├── kaspr/, hunter/, lemlist/ # Enrichment & outreach
50
+ │ └── folk/, slack/, resend/ # CRM & messaging
51
+ ├── skills/ # Claude Code skills
52
+ │ └── oto-*/SKILL.md # LLM instruction manuals
53
+ └── pyproject.toml # entry point: oto = "oto.cli:main"
54
+ ```
55
+
56
+ ## Adding a new connector
57
+
58
+ A connector = 3 files:
59
+
60
+ 1. **`commands/myservice.py`** — Typer app, exports `app`
61
+ 2. **`tools/myservice/`** — API client(s)
62
+ 3. **`skills/oto-myservice/SKILL.md`** — LLM instructions
63
+
64
+ See `docs/create-connector.md` for details.
65
+
66
+ ## Command pattern
67
+
68
+ Each `commands/*.py` file:
69
+ ```python
70
+ import typer
71
+ import json
72
+ from typing import Optional
73
+
74
+ app = typer.Typer(help="My service description")
75
+
76
+ @app.command("do-thing")
77
+ def do_thing(
78
+ query: str = typer.Argument(..., help="What to do"),
79
+ max_results: int = typer.Option(20, "--max-results", "-n"),
80
+ ):
81
+ """Do a thing."""
82
+ from oto.tools.myservice.client import MyServiceClient
83
+ client = MyServiceClient()
84
+ results = client.do_thing(query=query, max_results=max_results)
85
+ print(json.dumps(results, indent=2))
86
+ ```
87
+
88
+ Key rules:
89
+ - `app = typer.Typer()` exported, auto-discovered by `cli.py`
90
+ - Tool imports **inside functions** (lazy) so the CLI stays fast
91
+ - Always `print(json.dumps(..., indent=2))` for output
92
+ - Missing secrets raise `ValueError`, caught by `main()` → clean stderr message
93
+
94
+ ## Secrets
95
+
96
+ 3-tier resolution (first found wins):
97
+ 1. Environment variables
98
+ 2. `.otomata/secrets.env` in project directory (walks up 4 levels)
99
+ 3. `~/.otomata/secrets.env` (user-level)
100
+
101
+ ## Google OAuth
102
+
103
+ Tokens stored in `~/.otomata/google-oauth-token-{name}.json`.
104
+
105
+ Add an account: `oto google auth <name>` — opens browser for OAuth flow.
106
+ List accounts: `oto google auth --list`.
107
+
108
+ ## Skills (Claude Code)
109
+
110
+ Skills = SKILL.md files in `skills/oto-*/`, symlinked to `~/.claude/skills/`.
111
+
112
+ ```bash
113
+ oto skills list # see status
114
+ oto skills enable --all # enable all
115
+ oto skills enable oto-google # enable one
116
+ oto skills disable oto-pennylane # disable one
117
+ ```
oto_cli-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Alexis Laporte
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
oto_cli-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,171 @@
1
+ Metadata-Version: 2.4
2
+ Name: oto-cli
3
+ Version: 1.0.0
4
+ Summary: CLI toolkit for AI agents — covers the long tail of SaaS APIs that don't have a CLI
5
+ Project-URL: Repository, https://github.com/AlexisLaporte/oto
6
+ Author: Alexis Laporte
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Keywords: ai-agents,api,automation,cli,llm
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Environment :: Console
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
14
+ Requires-Python: >=3.10
15
+ Requires-Dist: python-dotenv>=1.0.0
16
+ Requires-Dist: requests>=2.28.0
17
+ Requires-Dist: typer>=0.9.0
18
+ Provides-Extra: all
19
+ Requires-Dist: anthropic>=0.40.0; extra == 'all'
20
+ Requires-Dist: gkeepapi>=0.16.0; extra == 'all'
21
+ Requires-Dist: google-api-python-client>=2.0.0; extra == 'all'
22
+ Requires-Dist: google-auth-oauthlib>=1.0.0; extra == 'all'
23
+ Requires-Dist: google-auth>=2.0.0; extra == 'all'
24
+ Requires-Dist: o-browser; extra == 'all'
25
+ Requires-Dist: pandas>=1.5.0; extra == 'all'
26
+ Requires-Dist: pyarrow>=10.0.0; extra == 'all'
27
+ Provides-Extra: anthropic
28
+ Requires-Dist: anthropic>=0.40.0; extra == 'anthropic'
29
+ Provides-Extra: browser
30
+ Requires-Dist: o-browser; extra == 'browser'
31
+ Provides-Extra: google
32
+ Requires-Dist: gkeepapi>=0.16.0; extra == 'google'
33
+ Requires-Dist: google-api-python-client>=2.0.0; extra == 'google'
34
+ Requires-Dist: google-auth-oauthlib>=1.0.0; extra == 'google'
35
+ Requires-Dist: google-auth>=2.0.0; extra == 'google'
36
+ Provides-Extra: stock
37
+ Requires-Dist: pandas>=1.5.0; extra == 'stock'
38
+ Requires-Dist: pyarrow>=10.0.0; extra == 'stock'
39
+ Description-Content-Type: text/markdown
40
+
41
+ # oto — CLI toolkit for AI agents
42
+
43
+ Your LLM already uses `gh` for GitHub, `aws` for AWS, `gcloud` for GCP.
44
+ **oto** covers the long tail — the SaaS products that don't have a CLI.
45
+
46
+ Each connector ships with a **SKILL.md** — an instruction manual that teaches your AI agent how to use it. Run `oto skills enable --all` and your Claude Code / Cursor / Aider gets instant context on 15+ APIs.
47
+
48
+ ## Why CLI over MCP?
49
+
50
+ | | CLI | MCP |
51
+ |---|---|---|
52
+ | Token cost | ~80 tokens (prompt + `--help`) | 4-32x more (full schema in context) |
53
+ | Reliability | 100% | ~72% ([source](https://scalekit.com)) |
54
+ | Setup | `pipx install oto-cli` | Server + transport + config |
55
+ | Composability | Pipes: `oto sirene search "fintech" \| jq '.[]'` | None |
56
+ | Works with | Every LLM, every framework | MCP-compatible clients only |
57
+
58
+ ## Installation
59
+
60
+ ```bash
61
+ # Standalone CLI
62
+ pipx install oto-cli
63
+
64
+ # With specific connectors
65
+ pipx install "oto-cli[google,browser]"
66
+
67
+ # All connectors
68
+ pipx install "oto-cli[all]"
69
+
70
+ # Development
71
+ git clone https://github.com/AlexisLaporte/oto.git
72
+ cd oto && pip install -e ".[all]"
73
+ ```
74
+
75
+ ## Connectors
76
+
77
+ | Command | What it does | Extra |
78
+ |---------|-------------|-------|
79
+ | `oto google` | Gmail, Drive, Docs, Sheets, Slides, Calendar | `google` |
80
+ | `oto browser` | LinkedIn, Crunchbase, Pappers, Indeed, G2 | `browser` |
81
+ | `oto notion` | Search, pages, databases | — |
82
+ | `oto sirene` | French company data (INSEE SIRENE) | — |
83
+ | `oto search` | Web & news search (Serper/Google) | — |
84
+ | `oto enrichment` | Contact enrichment (Kaspr, Hunter, Lemlist) | — |
85
+ | `oto pennylane` | Accounting (Pennylane API) | — |
86
+ | `oto anthropic` | API usage & cost tracking | `anthropic` |
87
+ | `oto whatsapp` | Send & read WhatsApp messages | — |
88
+ | `oto folk` | Folk CRM (contacts, companies, deals) | — |
89
+ | `oto company` | French company lookup (multi-source) | — |
90
+ | `oto audio` | Audio recording, transcription, summaries | — |
91
+
92
+ Connectors without an "Extra" only need `requests` (included in base install).
93
+
94
+ ## Quick start
95
+
96
+ ```bash
97
+ # Configure secrets
98
+ mkdir -p ~/.otomata
99
+ cat > ~/.otomata/secrets.env << 'EOF'
100
+ SERPER_API_KEY=xxx
101
+ NOTION_API_KEY=secret_xxx
102
+ SIRENE_API_KEY=xxx
103
+ EOF
104
+
105
+ # Check config
106
+ oto config
107
+
108
+ # Search the web
109
+ oto search web "AI agents 2026"
110
+
111
+ # Google OAuth setup (per account)
112
+ oto google auth myaccount
113
+ oto google gmail-search "from:bob" -a myaccount
114
+
115
+ # Browse LinkedIn
116
+ oto browser linkedin profile https://linkedin.com/in/someone
117
+
118
+ # French company data
119
+ oto sirene search "fintech"
120
+ ```
121
+
122
+ ## Skills for AI agents
123
+
124
+ The killer feature: each connector comes with a SKILL.md that teaches your LLM how to use it.
125
+
126
+ ```bash
127
+ # Enable all skills for Claude Code
128
+ oto skills enable --all
129
+
130
+ # Or pick specific ones
131
+ oto skills enable oto-google
132
+ oto skills enable oto-search
133
+ ```
134
+
135
+ Once enabled, your AI agent sees these instructions in its context and knows exactly which `oto` commands to use, with what arguments, and what output to expect.
136
+
137
+ ## Create your own connector
138
+
139
+ A connector is 3 files:
140
+
141
+ ```
142
+ oto/commands/myservice.py # CLI commands (Typer app)
143
+ oto/tools/myservice/ # API client
144
+ skills/oto-myservice/SKILL.md # LLM instructions
145
+ ```
146
+
147
+ See [docs/create-connector.md](docs/create-connector.md) for the full guide.
148
+
149
+ ## How it works
150
+
151
+ **Auto-discovery** — `cli.py` scans `commands/` at startup. Any Python file exporting a Typer `app` becomes a sub-command. No registry, no config. Drop a file, it appears in `oto --help`.
152
+
153
+ **Three types of connectors** (same CLI contract: JSON stdout, lazy imports, `get_secret()`):
154
+ - **API** — call REST APIs via `requests`. Each client handles auth/pagination/rate-limiting as the API requires. (notion, sirene, search, pennylane, folk...)
155
+ - **Browser** — automate a real browser for sites without an API. Async, require `oto-cli[browser]`. (linkedin, crunchbase, pappers, indeed, g2)
156
+ - **SDK** — use an official client library. Currently Google Workspace via `google-api-python-client` + OAuth2. Require `oto-cli[google]`.
157
+
158
+ **Secrets** — 3-tier resolution (first found wins):
159
+ 1. Environment variables
160
+ 2. `.otomata/secrets.env` in project directory (walks up 4 levels)
161
+ 3. `~/.otomata/secrets.env` (user-level)
162
+
163
+ **Output contract** — every command prints JSON to stdout, errors to stderr. Composable with `jq`, pipes, scripts.
164
+
165
+ **Lazy imports** — tool clients are imported inside functions, so the CLI starts fast regardless of how many connectors are installed.
166
+
167
+ See [docs/concepts.md](docs/concepts.md) for the full architecture guide.
168
+
169
+ ## License
170
+
171
+ MIT
@@ -0,0 +1,131 @@
1
+ # oto — CLI toolkit for AI agents
2
+
3
+ Your LLM already uses `gh` for GitHub, `aws` for AWS, `gcloud` for GCP.
4
+ **oto** covers the long tail — the SaaS products that don't have a CLI.
5
+
6
+ Each connector ships with a **SKILL.md** — an instruction manual that teaches your AI agent how to use it. Run `oto skills enable --all` and your Claude Code / Cursor / Aider gets instant context on 15+ APIs.
7
+
8
+ ## Why CLI over MCP?
9
+
10
+ | | CLI | MCP |
11
+ |---|---|---|
12
+ | Token cost | ~80 tokens (prompt + `--help`) | 4-32x more (full schema in context) |
13
+ | Reliability | 100% | ~72% ([source](https://scalekit.com)) |
14
+ | Setup | `pipx install oto-cli` | Server + transport + config |
15
+ | Composability | Pipes: `oto sirene search "fintech" \| jq '.[]'` | None |
16
+ | Works with | Every LLM, every framework | MCP-compatible clients only |
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ # Standalone CLI
22
+ pipx install oto-cli
23
+
24
+ # With specific connectors
25
+ pipx install "oto-cli[google,browser]"
26
+
27
+ # All connectors
28
+ pipx install "oto-cli[all]"
29
+
30
+ # Development
31
+ git clone https://github.com/AlexisLaporte/oto.git
32
+ cd oto && pip install -e ".[all]"
33
+ ```
34
+
35
+ ## Connectors
36
+
37
+ | Command | What it does | Extra |
38
+ |---------|-------------|-------|
39
+ | `oto google` | Gmail, Drive, Docs, Sheets, Slides, Calendar | `google` |
40
+ | `oto browser` | LinkedIn, Crunchbase, Pappers, Indeed, G2 | `browser` |
41
+ | `oto notion` | Search, pages, databases | — |
42
+ | `oto sirene` | French company data (INSEE SIRENE) | — |
43
+ | `oto search` | Web & news search (Serper/Google) | — |
44
+ | `oto enrichment` | Contact enrichment (Kaspr, Hunter, Lemlist) | — |
45
+ | `oto pennylane` | Accounting (Pennylane API) | — |
46
+ | `oto anthropic` | API usage & cost tracking | `anthropic` |
47
+ | `oto whatsapp` | Send & read WhatsApp messages | — |
48
+ | `oto folk` | Folk CRM (contacts, companies, deals) | — |
49
+ | `oto company` | French company lookup (multi-source) | — |
50
+ | `oto audio` | Audio recording, transcription, summaries | — |
51
+
52
+ Connectors without an "Extra" only need `requests` (included in base install).
53
+
54
+ ## Quick start
55
+
56
+ ```bash
57
+ # Configure secrets
58
+ mkdir -p ~/.otomata
59
+ cat > ~/.otomata/secrets.env << 'EOF'
60
+ SERPER_API_KEY=xxx
61
+ NOTION_API_KEY=secret_xxx
62
+ SIRENE_API_KEY=xxx
63
+ EOF
64
+
65
+ # Check config
66
+ oto config
67
+
68
+ # Search the web
69
+ oto search web "AI agents 2026"
70
+
71
+ # Google OAuth setup (per account)
72
+ oto google auth myaccount
73
+ oto google gmail-search "from:bob" -a myaccount
74
+
75
+ # Browse LinkedIn
76
+ oto browser linkedin profile https://linkedin.com/in/someone
77
+
78
+ # French company data
79
+ oto sirene search "fintech"
80
+ ```
81
+
82
+ ## Skills for AI agents
83
+
84
+ The killer feature: each connector comes with a SKILL.md that teaches your LLM how to use it.
85
+
86
+ ```bash
87
+ # Enable all skills for Claude Code
88
+ oto skills enable --all
89
+
90
+ # Or pick specific ones
91
+ oto skills enable oto-google
92
+ oto skills enable oto-search
93
+ ```
94
+
95
+ Once enabled, your AI agent sees these instructions in its context and knows exactly which `oto` commands to use, with what arguments, and what output to expect.
96
+
97
+ ## Create your own connector
98
+
99
+ A connector is 3 files:
100
+
101
+ ```
102
+ oto/commands/myservice.py # CLI commands (Typer app)
103
+ oto/tools/myservice/ # API client
104
+ skills/oto-myservice/SKILL.md # LLM instructions
105
+ ```
106
+
107
+ See [docs/create-connector.md](docs/create-connector.md) for the full guide.
108
+
109
+ ## How it works
110
+
111
+ **Auto-discovery** — `cli.py` scans `commands/` at startup. Any Python file exporting a Typer `app` becomes a sub-command. No registry, no config. Drop a file, it appears in `oto --help`.
112
+
113
+ **Three types of connectors** (same CLI contract: JSON stdout, lazy imports, `get_secret()`):
114
+ - **API** — call REST APIs via `requests`. Each client handles auth/pagination/rate-limiting as the API requires. (notion, sirene, search, pennylane, folk...)
115
+ - **Browser** — automate a real browser for sites without an API. Async, require `oto-cli[browser]`. (linkedin, crunchbase, pappers, indeed, g2)
116
+ - **SDK** — use an official client library. Currently Google Workspace via `google-api-python-client` + OAuth2. Require `oto-cli[google]`.
117
+
118
+ **Secrets** — 3-tier resolution (first found wins):
119
+ 1. Environment variables
120
+ 2. `.otomata/secrets.env` in project directory (walks up 4 levels)
121
+ 3. `~/.otomata/secrets.env` (user-level)
122
+
123
+ **Output contract** — every command prints JSON to stdout, errors to stderr. Composable with `jq`, pipes, scripts.
124
+
125
+ **Lazy imports** — tool clients are imported inside functions, so the CLI starts fast regardless of how many connectors are installed.
126
+
127
+ See [docs/concepts.md](docs/concepts.md) for the full architecture guide.
128
+
129
+ ## License
130
+
131
+ MIT
oto_cli-1.0.0/TODO.md ADDED
@@ -0,0 +1,45 @@
1
+ # TODO / Roadmap
2
+
3
+ ## Browser automation
4
+
5
+ ### Multi-backend support
6
+ Currently using Patchright (undetectable Playwright). Consider adding alternative backends:
7
+
8
+ - **Vercel Agent-Browser** (https://github.com/vercel-labs/agent-browser)
9
+ - CLI Rust/Node, fast
10
+ - `snapshot` command returns accessibility tree (AI-friendly)
11
+ - Stateful across commands
12
+ - `--profile` for session persistence
13
+ - Installation: `npm install -g agent-browser`
14
+
15
+ Could be useful for AI agent workflows where we want structured DOM info rather than raw HTML/text.
16
+
17
+ - **Chrome DevTools MCP** (https://github.com/anthropics/chrome-devtools-mcp)
18
+ - 26 outils : navigation, input, debugging, network, performance, emulation
19
+ - Accès direct aux DevTools (console, network, DOM)
20
+ - Tests E2E déclaratifs en YAML
21
+ - Installation: `claude mcp add chrome-devtools npx chrome-devtools-mcp@latest`
22
+
23
+ Complémentaire à Patchright : utile pour debugging dev web, pas pour scraping (nécessite Chrome UI).
24
+
25
+ ### Ideas
26
+ - [ ] Abstract backend selection in BrowserClient (patchright vs agent-browser)
27
+ - [ ] Add `snapshot()` method that returns structured accessibility tree (format AI-friendly)
28
+ - [ ] Évaluer Chrome DevTools MCP pour tests E2E (complémentaire à Patchright)
29
+
30
+ ## Scrapers
31
+
32
+ - [x] Indeed jobs scraper
33
+ - [x] LinkedIn profile scraper
34
+ - [x] Crunchbase, Pappers, G2 scrapers
35
+ - [ ] Generic "login + navigate + extract" pattern
36
+
37
+ ## Google tools
38
+
39
+ - [ ] Calendar integration
40
+ - [ ] Gmail (or use MCP server?)
41
+
42
+ ## Notion tools
43
+
44
+ - [ ] Batch page creation
45
+ - [ ] Database sync with external sources
@@ -0,0 +1,136 @@
1
+ # Concepts
2
+
3
+ ## Why oto exists
4
+
5
+ LLMs are good at using CLI tools — they were trained on billions of terminal interactions. For popular tools (`gh`, `aws`, `gcloud`, `docker`), the LLM already knows the commands.
6
+
7
+ But most SaaS products don't have a CLI. When an AI agent needs to interact with Notion, Pennylane, LinkedIn, or SIRENE, it has two options:
8
+ 1. **MCP** — a protocol that puts the full API schema in the LLM's context window (expensive: 4-32x more tokens, 72% reliability)
9
+ 2. **CLI** — a command-line tool the LLM calls via Bash (cheap: ~80 tokens, 100% reliability)
10
+
11
+ **oto is option 2.** One CLI, many connectors, each with an LLM instruction manual (SKILL.md).
12
+
13
+ ## Architecture
14
+
15
+ ### Auto-discovery
16
+
17
+ `cli.py` scans `oto/commands/` at startup. Any `.py` file that exports `app = typer.Typer()` becomes a sub-command:
18
+
19
+ ```
20
+ commands/google.py → oto google ...
21
+ commands/search.py → oto search ...
22
+ commands/stripe.py → oto stripe ... (you add this)
23
+ ```
24
+
25
+ No registry, no config. Drop a file, it works.
26
+
27
+ ### Connector anatomy
28
+
29
+ A connector is 3 parts:
30
+
31
+ ```
32
+ commands/myservice.py # CLI layer (Typer commands)
33
+ tools/myservice/client.py # API client (business logic)
34
+ skills/oto-myservice/SKILL.md # LLM instructions
35
+ ```
36
+
37
+ **Commands** define the CLI surface — arguments, options, help text. They import the tool client lazily (inside the function body) so the CLI starts fast.
38
+
39
+ **Tool clients** talk to the API. They use `get_secret()` for auth and return plain Python dicts/lists.
40
+
41
+ **Skills** are markdown files that teach the LLM how to use the connector. They get symlinked into `~/.claude/skills/` and appear in the agent's context.
42
+
43
+ ### Connector types
44
+
45
+ There are 3 types of connectors. They differ in how the tool client talks to the service, but the CLI contract is always the same: JSON on stdout, lazy imports, `get_secret()` for auth.
46
+
47
+ **API connectors** — call a REST API via `requests`. Most connectors are this type. Auth, rate limiting, and pagination depend on the provider — each client handles it as the API requires.
48
+
49
+ ```
50
+ tools/serper/client.py → requests + X-API-KEY header
51
+ tools/notion/client.py → requests + Bearer token + cursor pagination
52
+ tools/pennylane/client.py → requests + Bearer token + retry on 429
53
+ tools/folk/client.py → requests + Bearer token + Retry-After header
54
+ tools/kaspr/client.py → requests + Bearer token
55
+ tools/hunter/client.py → requests + query param api_key
56
+ ```
57
+
58
+ **Browser connectors** — automate a real browser (via [o-browser](https://github.com/AntMusic/o-browser)) for sites that don't have an API, or where the API is too limited. They inherit from `BrowserClient`, are async, and use context managers. Commands wrap them in `asyncio.run()`.
59
+
60
+ ```
61
+ tools/browser/linkedin.py → cookie auth, file-based rate limiting
62
+ tools/browser/crunchbase.py → cookie auth, session persistence
63
+ tools/browser/pappers.py → no auth, Cloudflare handling
64
+ tools/browser/indeed.py → no auth, multi-country
65
+ tools/browser/g2.py → no auth, review scraping
66
+ ```
67
+
68
+ Require the `browser` extra: `pip install oto-cli[browser]`.
69
+
70
+ **SDK connectors** — use an official SDK instead of raw HTTP. Currently only Google Workspace, which uses `google-api-python-client` with OAuth2 tokens stored in `~/.otomata/google-oauth-token-{name}.json`.
71
+
72
+ ```
73
+ tools/google/gmail/ → google-api-python-client (OAuth2)
74
+ tools/google/drive/ → google-api-python-client (OAuth2)
75
+ tools/google/docs/ → google-api-python-client (OAuth2)
76
+ tools/google/calendar/ → google-api-python-client (OAuth2)
77
+ ```
78
+
79
+ Require the `google` extra: `pip install oto-cli[google]`.
80
+
81
+ **There's no base class.** Each client implements what its API requires — the only shared contract is on the CLI side (JSON output, lazy imports, `get_secret()`).
82
+
83
+ ### Secrets
84
+
85
+ 3-tier resolution — first found wins:
86
+
87
+ | Priority | Location | Scope |
88
+ |----------|----------|-------|
89
+ | 1 | Environment variable | Session |
90
+ | 2 | `.otomata/secrets.env` (project dir, walks up 4 levels) | Project |
91
+ | 3 | `~/.otomata/secrets.env` | User |
92
+
93
+ ```bash
94
+ # Setup
95
+ mkdir -p ~/.otomata
96
+ echo "SERPER_API_KEY=xxx" >> ~/.otomata/secrets.env
97
+
98
+ # Check
99
+ oto config
100
+ ```
101
+
102
+ In code, `get_secret("SERPER_API_KEY")` resolves through all 3 tiers. If the key is missing, it raises a `ValueError` with instructions — caught by `main()` for a clean error message.
103
+
104
+ ### Output contract
105
+
106
+ Every command prints JSON to stdout:
107
+
108
+ ```bash
109
+ oto search web "AI agents" | jq '.[0].title'
110
+ oto sirene search "fintech" | jq '.[] | .siren'
111
+ ```
112
+
113
+ Errors go to stderr. Exit code 0 = success, 1 = error. This makes oto composable with standard Unix tools.
114
+
115
+ ### Skills (SKILL.md)
116
+
117
+ A SKILL.md is a markdown file with YAML frontmatter:
118
+
119
+ ```markdown
120
+ ---
121
+ name: oto-search
122
+ description: Web and news search via Serper (Google)
123
+ ---
124
+
125
+ # Search
126
+ Use `oto search` commands via Bash.
127
+ ...
128
+ ```
129
+
130
+ When symlinked to `~/.claude/skills/`, the LLM sees this file in its context and learns when and how to use the connector. The `description` field is what the LLM uses to decide if the skill is relevant.
131
+
132
+ ```bash
133
+ oto skills enable oto-search # creates symlink
134
+ oto skills disable oto-search # removes symlink
135
+ oto skills list # shows status
136
+ ```