PraisonAI 3.0.0__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 (393) hide show
  1. praisonai/__init__.py +54 -0
  2. praisonai/__main__.py +15 -0
  3. praisonai/acp/__init__.py +54 -0
  4. praisonai/acp/config.py +159 -0
  5. praisonai/acp/server.py +587 -0
  6. praisonai/acp/session.py +219 -0
  7. praisonai/adapters/__init__.py +50 -0
  8. praisonai/adapters/readers.py +395 -0
  9. praisonai/adapters/rerankers.py +315 -0
  10. praisonai/adapters/retrievers.py +394 -0
  11. praisonai/adapters/vector_stores.py +409 -0
  12. praisonai/agent_scheduler.py +337 -0
  13. praisonai/agents_generator.py +903 -0
  14. praisonai/api/call.py +292 -0
  15. praisonai/auto.py +1197 -0
  16. praisonai/capabilities/__init__.py +275 -0
  17. praisonai/capabilities/a2a.py +140 -0
  18. praisonai/capabilities/assistants.py +283 -0
  19. praisonai/capabilities/audio.py +320 -0
  20. praisonai/capabilities/batches.py +469 -0
  21. praisonai/capabilities/completions.py +336 -0
  22. praisonai/capabilities/container_files.py +155 -0
  23. praisonai/capabilities/containers.py +93 -0
  24. praisonai/capabilities/embeddings.py +158 -0
  25. praisonai/capabilities/files.py +467 -0
  26. praisonai/capabilities/fine_tuning.py +293 -0
  27. praisonai/capabilities/guardrails.py +182 -0
  28. praisonai/capabilities/images.py +330 -0
  29. praisonai/capabilities/mcp.py +190 -0
  30. praisonai/capabilities/messages.py +270 -0
  31. praisonai/capabilities/moderations.py +154 -0
  32. praisonai/capabilities/ocr.py +217 -0
  33. praisonai/capabilities/passthrough.py +204 -0
  34. praisonai/capabilities/rag.py +207 -0
  35. praisonai/capabilities/realtime.py +160 -0
  36. praisonai/capabilities/rerank.py +165 -0
  37. praisonai/capabilities/responses.py +266 -0
  38. praisonai/capabilities/search.py +109 -0
  39. praisonai/capabilities/skills.py +133 -0
  40. praisonai/capabilities/vector_store_files.py +334 -0
  41. praisonai/capabilities/vector_stores.py +304 -0
  42. praisonai/capabilities/videos.py +141 -0
  43. praisonai/chainlit_ui.py +304 -0
  44. praisonai/chat/__init__.py +106 -0
  45. praisonai/chat/app.py +125 -0
  46. praisonai/cli/__init__.py +26 -0
  47. praisonai/cli/app.py +213 -0
  48. praisonai/cli/commands/__init__.py +75 -0
  49. praisonai/cli/commands/acp.py +70 -0
  50. praisonai/cli/commands/completion.py +333 -0
  51. praisonai/cli/commands/config.py +166 -0
  52. praisonai/cli/commands/debug.py +142 -0
  53. praisonai/cli/commands/diag.py +55 -0
  54. praisonai/cli/commands/doctor.py +166 -0
  55. praisonai/cli/commands/environment.py +179 -0
  56. praisonai/cli/commands/lsp.py +112 -0
  57. praisonai/cli/commands/mcp.py +210 -0
  58. praisonai/cli/commands/profile.py +457 -0
  59. praisonai/cli/commands/run.py +228 -0
  60. praisonai/cli/commands/schedule.py +150 -0
  61. praisonai/cli/commands/serve.py +97 -0
  62. praisonai/cli/commands/session.py +212 -0
  63. praisonai/cli/commands/traces.py +145 -0
  64. praisonai/cli/commands/version.py +101 -0
  65. praisonai/cli/configuration/__init__.py +18 -0
  66. praisonai/cli/configuration/loader.py +353 -0
  67. praisonai/cli/configuration/paths.py +114 -0
  68. praisonai/cli/configuration/schema.py +164 -0
  69. praisonai/cli/features/__init__.py +268 -0
  70. praisonai/cli/features/acp.py +236 -0
  71. praisonai/cli/features/action_orchestrator.py +546 -0
  72. praisonai/cli/features/agent_scheduler.py +773 -0
  73. praisonai/cli/features/agent_tools.py +474 -0
  74. praisonai/cli/features/agents.py +375 -0
  75. praisonai/cli/features/at_mentions.py +471 -0
  76. praisonai/cli/features/auto_memory.py +182 -0
  77. praisonai/cli/features/autonomy_mode.py +490 -0
  78. praisonai/cli/features/background.py +356 -0
  79. praisonai/cli/features/base.py +168 -0
  80. praisonai/cli/features/capabilities.py +1326 -0
  81. praisonai/cli/features/checkpoints.py +338 -0
  82. praisonai/cli/features/code_intelligence.py +652 -0
  83. praisonai/cli/features/compaction.py +294 -0
  84. praisonai/cli/features/compare.py +534 -0
  85. praisonai/cli/features/cost_tracker.py +514 -0
  86. praisonai/cli/features/debug.py +810 -0
  87. praisonai/cli/features/deploy.py +517 -0
  88. praisonai/cli/features/diag.py +289 -0
  89. praisonai/cli/features/doctor/__init__.py +63 -0
  90. praisonai/cli/features/doctor/checks/__init__.py +24 -0
  91. praisonai/cli/features/doctor/checks/acp_checks.py +240 -0
  92. praisonai/cli/features/doctor/checks/config_checks.py +366 -0
  93. praisonai/cli/features/doctor/checks/db_checks.py +366 -0
  94. praisonai/cli/features/doctor/checks/env_checks.py +543 -0
  95. praisonai/cli/features/doctor/checks/lsp_checks.py +199 -0
  96. praisonai/cli/features/doctor/checks/mcp_checks.py +349 -0
  97. praisonai/cli/features/doctor/checks/memory_checks.py +268 -0
  98. praisonai/cli/features/doctor/checks/network_checks.py +251 -0
  99. praisonai/cli/features/doctor/checks/obs_checks.py +328 -0
  100. praisonai/cli/features/doctor/checks/performance_checks.py +235 -0
  101. praisonai/cli/features/doctor/checks/permissions_checks.py +259 -0
  102. praisonai/cli/features/doctor/checks/selftest_checks.py +322 -0
  103. praisonai/cli/features/doctor/checks/serve_checks.py +426 -0
  104. praisonai/cli/features/doctor/checks/skills_checks.py +231 -0
  105. praisonai/cli/features/doctor/checks/tools_checks.py +371 -0
  106. praisonai/cli/features/doctor/engine.py +266 -0
  107. praisonai/cli/features/doctor/formatters.py +310 -0
  108. praisonai/cli/features/doctor/handler.py +397 -0
  109. praisonai/cli/features/doctor/models.py +264 -0
  110. praisonai/cli/features/doctor/registry.py +239 -0
  111. praisonai/cli/features/endpoints.py +1019 -0
  112. praisonai/cli/features/eval.py +560 -0
  113. praisonai/cli/features/external_agents.py +231 -0
  114. praisonai/cli/features/fast_context.py +410 -0
  115. praisonai/cli/features/flow_display.py +566 -0
  116. praisonai/cli/features/git_integration.py +651 -0
  117. praisonai/cli/features/guardrail.py +171 -0
  118. praisonai/cli/features/handoff.py +185 -0
  119. praisonai/cli/features/hooks.py +583 -0
  120. praisonai/cli/features/image.py +384 -0
  121. praisonai/cli/features/interactive_runtime.py +585 -0
  122. praisonai/cli/features/interactive_tools.py +380 -0
  123. praisonai/cli/features/interactive_tui.py +603 -0
  124. praisonai/cli/features/jobs.py +632 -0
  125. praisonai/cli/features/knowledge.py +531 -0
  126. praisonai/cli/features/lite.py +244 -0
  127. praisonai/cli/features/lsp_cli.py +225 -0
  128. praisonai/cli/features/mcp.py +169 -0
  129. praisonai/cli/features/message_queue.py +587 -0
  130. praisonai/cli/features/metrics.py +211 -0
  131. praisonai/cli/features/n8n.py +673 -0
  132. praisonai/cli/features/observability.py +293 -0
  133. praisonai/cli/features/ollama.py +361 -0
  134. praisonai/cli/features/output_style.py +273 -0
  135. praisonai/cli/features/package.py +631 -0
  136. praisonai/cli/features/performance.py +308 -0
  137. praisonai/cli/features/persistence.py +636 -0
  138. praisonai/cli/features/profile.py +226 -0
  139. praisonai/cli/features/profiler/__init__.py +81 -0
  140. praisonai/cli/features/profiler/core.py +558 -0
  141. praisonai/cli/features/profiler/optimizations.py +652 -0
  142. praisonai/cli/features/profiler/suite.py +386 -0
  143. praisonai/cli/features/profiling.py +350 -0
  144. praisonai/cli/features/queue/__init__.py +73 -0
  145. praisonai/cli/features/queue/manager.py +395 -0
  146. praisonai/cli/features/queue/models.py +286 -0
  147. praisonai/cli/features/queue/persistence.py +564 -0
  148. praisonai/cli/features/queue/scheduler.py +484 -0
  149. praisonai/cli/features/queue/worker.py +372 -0
  150. praisonai/cli/features/recipe.py +1723 -0
  151. praisonai/cli/features/recipes.py +449 -0
  152. praisonai/cli/features/registry.py +229 -0
  153. praisonai/cli/features/repo_map.py +860 -0
  154. praisonai/cli/features/router.py +466 -0
  155. praisonai/cli/features/sandbox_executor.py +515 -0
  156. praisonai/cli/features/serve.py +829 -0
  157. praisonai/cli/features/session.py +222 -0
  158. praisonai/cli/features/skills.py +856 -0
  159. praisonai/cli/features/slash_commands.py +650 -0
  160. praisonai/cli/features/telemetry.py +179 -0
  161. praisonai/cli/features/templates.py +1384 -0
  162. praisonai/cli/features/thinking.py +305 -0
  163. praisonai/cli/features/todo.py +334 -0
  164. praisonai/cli/features/tools.py +680 -0
  165. praisonai/cli/features/tui/__init__.py +83 -0
  166. praisonai/cli/features/tui/app.py +580 -0
  167. praisonai/cli/features/tui/cli.py +566 -0
  168. praisonai/cli/features/tui/debug.py +511 -0
  169. praisonai/cli/features/tui/events.py +99 -0
  170. praisonai/cli/features/tui/mock_provider.py +328 -0
  171. praisonai/cli/features/tui/orchestrator.py +652 -0
  172. praisonai/cli/features/tui/screens/__init__.py +50 -0
  173. praisonai/cli/features/tui/screens/main.py +245 -0
  174. praisonai/cli/features/tui/screens/queue.py +174 -0
  175. praisonai/cli/features/tui/screens/session.py +124 -0
  176. praisonai/cli/features/tui/screens/settings.py +148 -0
  177. praisonai/cli/features/tui/widgets/__init__.py +56 -0
  178. praisonai/cli/features/tui/widgets/chat.py +261 -0
  179. praisonai/cli/features/tui/widgets/composer.py +224 -0
  180. praisonai/cli/features/tui/widgets/queue_panel.py +200 -0
  181. praisonai/cli/features/tui/widgets/status.py +167 -0
  182. praisonai/cli/features/tui/widgets/tool_panel.py +248 -0
  183. praisonai/cli/features/workflow.py +720 -0
  184. praisonai/cli/legacy.py +236 -0
  185. praisonai/cli/main.py +5559 -0
  186. praisonai/cli/schedule_cli.py +54 -0
  187. praisonai/cli/state/__init__.py +31 -0
  188. praisonai/cli/state/identifiers.py +161 -0
  189. praisonai/cli/state/sessions.py +313 -0
  190. praisonai/code/__init__.py +93 -0
  191. praisonai/code/agent_tools.py +344 -0
  192. praisonai/code/diff/__init__.py +21 -0
  193. praisonai/code/diff/diff_strategy.py +432 -0
  194. praisonai/code/tools/__init__.py +27 -0
  195. praisonai/code/tools/apply_diff.py +221 -0
  196. praisonai/code/tools/execute_command.py +275 -0
  197. praisonai/code/tools/list_files.py +274 -0
  198. praisonai/code/tools/read_file.py +206 -0
  199. praisonai/code/tools/search_replace.py +248 -0
  200. praisonai/code/tools/write_file.py +217 -0
  201. praisonai/code/utils/__init__.py +46 -0
  202. praisonai/code/utils/file_utils.py +307 -0
  203. praisonai/code/utils/ignore_utils.py +308 -0
  204. praisonai/code/utils/text_utils.py +276 -0
  205. praisonai/db/__init__.py +64 -0
  206. praisonai/db/adapter.py +531 -0
  207. praisonai/deploy/__init__.py +62 -0
  208. praisonai/deploy/api.py +231 -0
  209. praisonai/deploy/docker.py +454 -0
  210. praisonai/deploy/doctor.py +367 -0
  211. praisonai/deploy/main.py +327 -0
  212. praisonai/deploy/models.py +179 -0
  213. praisonai/deploy/providers/__init__.py +33 -0
  214. praisonai/deploy/providers/aws.py +331 -0
  215. praisonai/deploy/providers/azure.py +358 -0
  216. praisonai/deploy/providers/base.py +101 -0
  217. praisonai/deploy/providers/gcp.py +314 -0
  218. praisonai/deploy/schema.py +208 -0
  219. praisonai/deploy.py +185 -0
  220. praisonai/endpoints/__init__.py +53 -0
  221. praisonai/endpoints/a2u_server.py +410 -0
  222. praisonai/endpoints/discovery.py +165 -0
  223. praisonai/endpoints/providers/__init__.py +28 -0
  224. praisonai/endpoints/providers/a2a.py +253 -0
  225. praisonai/endpoints/providers/a2u.py +208 -0
  226. praisonai/endpoints/providers/agents_api.py +171 -0
  227. praisonai/endpoints/providers/base.py +231 -0
  228. praisonai/endpoints/providers/mcp.py +263 -0
  229. praisonai/endpoints/providers/recipe.py +206 -0
  230. praisonai/endpoints/providers/tools_mcp.py +150 -0
  231. praisonai/endpoints/registry.py +131 -0
  232. praisonai/endpoints/server.py +161 -0
  233. praisonai/inbuilt_tools/__init__.py +24 -0
  234. praisonai/inbuilt_tools/autogen_tools.py +117 -0
  235. praisonai/inc/__init__.py +2 -0
  236. praisonai/inc/config.py +96 -0
  237. praisonai/inc/models.py +155 -0
  238. praisonai/integrations/__init__.py +56 -0
  239. praisonai/integrations/base.py +303 -0
  240. praisonai/integrations/claude_code.py +270 -0
  241. praisonai/integrations/codex_cli.py +255 -0
  242. praisonai/integrations/cursor_cli.py +195 -0
  243. praisonai/integrations/gemini_cli.py +222 -0
  244. praisonai/jobs/__init__.py +67 -0
  245. praisonai/jobs/executor.py +425 -0
  246. praisonai/jobs/models.py +230 -0
  247. praisonai/jobs/router.py +314 -0
  248. praisonai/jobs/server.py +186 -0
  249. praisonai/jobs/store.py +203 -0
  250. praisonai/llm/__init__.py +66 -0
  251. praisonai/llm/registry.py +382 -0
  252. praisonai/mcp_server/__init__.py +152 -0
  253. praisonai/mcp_server/adapters/__init__.py +74 -0
  254. praisonai/mcp_server/adapters/agents.py +128 -0
  255. praisonai/mcp_server/adapters/capabilities.py +168 -0
  256. praisonai/mcp_server/adapters/cli_tools.py +568 -0
  257. praisonai/mcp_server/adapters/extended_capabilities.py +462 -0
  258. praisonai/mcp_server/adapters/knowledge.py +93 -0
  259. praisonai/mcp_server/adapters/memory.py +104 -0
  260. praisonai/mcp_server/adapters/prompts.py +306 -0
  261. praisonai/mcp_server/adapters/resources.py +124 -0
  262. praisonai/mcp_server/adapters/tools_bridge.py +280 -0
  263. praisonai/mcp_server/auth/__init__.py +48 -0
  264. praisonai/mcp_server/auth/api_key.py +291 -0
  265. praisonai/mcp_server/auth/oauth.py +460 -0
  266. praisonai/mcp_server/auth/oidc.py +289 -0
  267. praisonai/mcp_server/auth/scopes.py +260 -0
  268. praisonai/mcp_server/cli.py +852 -0
  269. praisonai/mcp_server/elicitation.py +445 -0
  270. praisonai/mcp_server/icons.py +302 -0
  271. praisonai/mcp_server/recipe_adapter.py +573 -0
  272. praisonai/mcp_server/recipe_cli.py +824 -0
  273. praisonai/mcp_server/registry.py +703 -0
  274. praisonai/mcp_server/sampling.py +422 -0
  275. praisonai/mcp_server/server.py +490 -0
  276. praisonai/mcp_server/tasks.py +443 -0
  277. praisonai/mcp_server/transports/__init__.py +18 -0
  278. praisonai/mcp_server/transports/http_stream.py +376 -0
  279. praisonai/mcp_server/transports/stdio.py +132 -0
  280. praisonai/persistence/__init__.py +84 -0
  281. praisonai/persistence/config.py +238 -0
  282. praisonai/persistence/conversation/__init__.py +25 -0
  283. praisonai/persistence/conversation/async_mysql.py +427 -0
  284. praisonai/persistence/conversation/async_postgres.py +410 -0
  285. praisonai/persistence/conversation/async_sqlite.py +371 -0
  286. praisonai/persistence/conversation/base.py +151 -0
  287. praisonai/persistence/conversation/json_store.py +250 -0
  288. praisonai/persistence/conversation/mysql.py +387 -0
  289. praisonai/persistence/conversation/postgres.py +401 -0
  290. praisonai/persistence/conversation/singlestore.py +240 -0
  291. praisonai/persistence/conversation/sqlite.py +341 -0
  292. praisonai/persistence/conversation/supabase.py +203 -0
  293. praisonai/persistence/conversation/surrealdb.py +287 -0
  294. praisonai/persistence/factory.py +301 -0
  295. praisonai/persistence/hooks/__init__.py +18 -0
  296. praisonai/persistence/hooks/agent_hooks.py +297 -0
  297. praisonai/persistence/knowledge/__init__.py +26 -0
  298. praisonai/persistence/knowledge/base.py +144 -0
  299. praisonai/persistence/knowledge/cassandra.py +232 -0
  300. praisonai/persistence/knowledge/chroma.py +295 -0
  301. praisonai/persistence/knowledge/clickhouse.py +242 -0
  302. praisonai/persistence/knowledge/cosmosdb_vector.py +438 -0
  303. praisonai/persistence/knowledge/couchbase.py +286 -0
  304. praisonai/persistence/knowledge/lancedb.py +216 -0
  305. praisonai/persistence/knowledge/langchain_adapter.py +291 -0
  306. praisonai/persistence/knowledge/lightrag_adapter.py +212 -0
  307. praisonai/persistence/knowledge/llamaindex_adapter.py +256 -0
  308. praisonai/persistence/knowledge/milvus.py +277 -0
  309. praisonai/persistence/knowledge/mongodb_vector.py +306 -0
  310. praisonai/persistence/knowledge/pgvector.py +335 -0
  311. praisonai/persistence/knowledge/pinecone.py +253 -0
  312. praisonai/persistence/knowledge/qdrant.py +301 -0
  313. praisonai/persistence/knowledge/redis_vector.py +291 -0
  314. praisonai/persistence/knowledge/singlestore_vector.py +299 -0
  315. praisonai/persistence/knowledge/surrealdb_vector.py +309 -0
  316. praisonai/persistence/knowledge/upstash_vector.py +266 -0
  317. praisonai/persistence/knowledge/weaviate.py +223 -0
  318. praisonai/persistence/migrations/__init__.py +10 -0
  319. praisonai/persistence/migrations/manager.py +251 -0
  320. praisonai/persistence/orchestrator.py +406 -0
  321. praisonai/persistence/state/__init__.py +21 -0
  322. praisonai/persistence/state/async_mongodb.py +200 -0
  323. praisonai/persistence/state/base.py +107 -0
  324. praisonai/persistence/state/dynamodb.py +226 -0
  325. praisonai/persistence/state/firestore.py +175 -0
  326. praisonai/persistence/state/gcs.py +155 -0
  327. praisonai/persistence/state/memory.py +245 -0
  328. praisonai/persistence/state/mongodb.py +158 -0
  329. praisonai/persistence/state/redis.py +190 -0
  330. praisonai/persistence/state/upstash.py +144 -0
  331. praisonai/persistence/tests/__init__.py +3 -0
  332. praisonai/persistence/tests/test_all_backends.py +633 -0
  333. praisonai/profiler.py +1214 -0
  334. praisonai/recipe/__init__.py +134 -0
  335. praisonai/recipe/bridge.py +278 -0
  336. praisonai/recipe/core.py +893 -0
  337. praisonai/recipe/exceptions.py +54 -0
  338. praisonai/recipe/history.py +402 -0
  339. praisonai/recipe/models.py +266 -0
  340. praisonai/recipe/operations.py +440 -0
  341. praisonai/recipe/policy.py +422 -0
  342. praisonai/recipe/registry.py +849 -0
  343. praisonai/recipe/runtime.py +214 -0
  344. praisonai/recipe/security.py +711 -0
  345. praisonai/recipe/serve.py +859 -0
  346. praisonai/recipe/server.py +613 -0
  347. praisonai/scheduler/__init__.py +45 -0
  348. praisonai/scheduler/agent_scheduler.py +552 -0
  349. praisonai/scheduler/base.py +124 -0
  350. praisonai/scheduler/daemon_manager.py +225 -0
  351. praisonai/scheduler/state_manager.py +155 -0
  352. praisonai/scheduler/yaml_loader.py +193 -0
  353. praisonai/scheduler.py +194 -0
  354. praisonai/setup/__init__.py +1 -0
  355. praisonai/setup/build.py +21 -0
  356. praisonai/setup/post_install.py +23 -0
  357. praisonai/setup/setup_conda_env.py +25 -0
  358. praisonai/setup.py +16 -0
  359. praisonai/templates/__init__.py +116 -0
  360. praisonai/templates/cache.py +364 -0
  361. praisonai/templates/dependency_checker.py +358 -0
  362. praisonai/templates/discovery.py +391 -0
  363. praisonai/templates/loader.py +564 -0
  364. praisonai/templates/registry.py +511 -0
  365. praisonai/templates/resolver.py +206 -0
  366. praisonai/templates/security.py +327 -0
  367. praisonai/templates/tool_override.py +498 -0
  368. praisonai/templates/tools_doctor.py +256 -0
  369. praisonai/test.py +105 -0
  370. praisonai/train.py +562 -0
  371. praisonai/train_vision.py +306 -0
  372. praisonai/ui/agents.py +824 -0
  373. praisonai/ui/callbacks.py +57 -0
  374. praisonai/ui/chainlit_compat.py +246 -0
  375. praisonai/ui/chat.py +532 -0
  376. praisonai/ui/code.py +717 -0
  377. praisonai/ui/colab.py +474 -0
  378. praisonai/ui/colab_chainlit.py +81 -0
  379. praisonai/ui/components/aicoder.py +284 -0
  380. praisonai/ui/context.py +283 -0
  381. praisonai/ui/database_config.py +56 -0
  382. praisonai/ui/db.py +294 -0
  383. praisonai/ui/realtime.py +488 -0
  384. praisonai/ui/realtimeclient/__init__.py +756 -0
  385. praisonai/ui/realtimeclient/tools.py +242 -0
  386. praisonai/ui/sql_alchemy.py +710 -0
  387. praisonai/upload_vision.py +140 -0
  388. praisonai/version.py +1 -0
  389. praisonai-3.0.0.dist-info/METADATA +3493 -0
  390. praisonai-3.0.0.dist-info/RECORD +393 -0
  391. praisonai-3.0.0.dist-info/WHEEL +5 -0
  392. praisonai-3.0.0.dist-info/entry_points.txt +4 -0
  393. praisonai-3.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,303 @@
1
+ """
2
+ Base class for external CLI tool integrations.
3
+
4
+ Provides a consistent interface for integrating with AI coding CLI tools
5
+ like Claude Code, Gemini CLI, Codex CLI, and Cursor CLI.
6
+
7
+ Features:
8
+ - Lazy availability checking with caching
9
+ - Async subprocess execution
10
+ - Tool wrapper for agent integration
11
+ - Timeout handling
12
+ """
13
+
14
+ from abc import ABC, abstractmethod
15
+ from typing import AsyncIterator, Optional, Dict, Any, Tuple, List
16
+ import asyncio
17
+ import shutil
18
+ import os
19
+
20
+
21
+ class BaseCLIIntegration(ABC):
22
+ """
23
+ Abstract base class for external CLI tool integrations.
24
+
25
+ Provides:
26
+ - Lazy availability checking with class-level caching
27
+ - Async subprocess execution with timeout handling
28
+ - Tool wrapper for PraisonAI agent integration
29
+
30
+ Subclasses must implement:
31
+ - cli_command property: The CLI command name
32
+ - execute method: Execute the CLI and return result
33
+ - stream method: Stream output from the CLI
34
+
35
+ Example:
36
+ class MyIntegration(BaseCLIIntegration):
37
+ @property
38
+ def cli_command(self) -> str:
39
+ return "my-cli"
40
+
41
+ async def execute(self, prompt: str, **options) -> str:
42
+ return await self.execute_async(["my-cli", "-p", prompt])
43
+
44
+ async def stream(self, prompt: str, **options):
45
+ async for line in self.stream_async(["my-cli", "-p", prompt]):
46
+ yield line
47
+ """
48
+
49
+ # Class-level cache for availability checks (shared across instances)
50
+ _availability_cache: Dict[str, bool] = {}
51
+
52
+ def __init__(self, workspace: str = ".", timeout: int = 300):
53
+ """
54
+ Initialize the CLI integration.
55
+
56
+ Args:
57
+ workspace: Working directory for CLI execution (default: current dir)
58
+ timeout: Default timeout in seconds for CLI execution (default: 300)
59
+ """
60
+ self.workspace = workspace
61
+ self.timeout = timeout
62
+
63
+ @property
64
+ @abstractmethod
65
+ def cli_command(self) -> str:
66
+ """
67
+ Return the CLI command name.
68
+
69
+ This is used for availability checking and tool naming.
70
+
71
+ Returns:
72
+ str: The CLI command (e.g., "claude", "gemini", "codex", "cursor-agent")
73
+ """
74
+ pass
75
+
76
+ @property
77
+ def is_available(self) -> bool:
78
+ """
79
+ Check if the CLI tool is installed and available.
80
+
81
+ Uses class-level caching to avoid repeated filesystem checks.
82
+
83
+ Returns:
84
+ bool: True if the CLI is available, False otherwise
85
+ """
86
+ if self.cli_command not in self._availability_cache:
87
+ self._availability_cache[self.cli_command] = shutil.which(self.cli_command) is not None
88
+ return self._availability_cache[self.cli_command]
89
+
90
+ @abstractmethod
91
+ async def execute(self, prompt: str, **options) -> str:
92
+ """
93
+ Execute the CLI tool and return the result.
94
+
95
+ Args:
96
+ prompt: The prompt/query to send to the CLI
97
+ **options: Additional options for the CLI
98
+
99
+ Returns:
100
+ str: The CLI output
101
+ """
102
+ pass
103
+
104
+ @abstractmethod
105
+ async def stream(self, prompt: str, **options) -> AsyncIterator[Dict[str, Any]]:
106
+ """
107
+ Stream output from the CLI tool.
108
+
109
+ Args:
110
+ prompt: The prompt/query to send to the CLI
111
+ **options: Additional options for the CLI
112
+
113
+ Yields:
114
+ dict: Parsed output events from the CLI
115
+ """
116
+ pass
117
+
118
+ async def execute_async(self, cmd: List[str], timeout: Optional[int] = None) -> str:
119
+ """
120
+ Execute a command asynchronously and return stdout.
121
+
122
+ Args:
123
+ cmd: Command and arguments as a list
124
+ timeout: Timeout in seconds (uses self.timeout if not specified)
125
+
126
+ Returns:
127
+ str: The command's stdout
128
+
129
+ Raises:
130
+ TimeoutError: If the command times out
131
+ """
132
+ timeout = timeout or self.timeout
133
+
134
+ proc = await asyncio.create_subprocess_exec(
135
+ *cmd,
136
+ stdout=asyncio.subprocess.PIPE,
137
+ stderr=asyncio.subprocess.PIPE,
138
+ cwd=self.workspace,
139
+ env=self.get_env()
140
+ )
141
+
142
+ try:
143
+ stdout, stderr = await asyncio.wait_for(
144
+ proc.communicate(),
145
+ timeout=timeout
146
+ )
147
+ return stdout.decode()
148
+ except asyncio.TimeoutError:
149
+ proc.kill()
150
+ await proc.wait()
151
+ raise TimeoutError(f"Command timed out after {timeout}s: {' '.join(cmd)}")
152
+
153
+ async def execute_async_with_stderr(
154
+ self,
155
+ cmd: List[str],
156
+ timeout: Optional[int] = None
157
+ ) -> Tuple[str, str]:
158
+ """
159
+ Execute a command asynchronously and return both stdout and stderr.
160
+
161
+ Args:
162
+ cmd: Command and arguments as a list
163
+ timeout: Timeout in seconds (uses self.timeout if not specified)
164
+
165
+ Returns:
166
+ Tuple[str, str]: (stdout, stderr)
167
+
168
+ Raises:
169
+ TimeoutError: If the command times out
170
+ """
171
+ timeout = timeout or self.timeout
172
+
173
+ proc = await asyncio.create_subprocess_exec(
174
+ *cmd,
175
+ stdout=asyncio.subprocess.PIPE,
176
+ stderr=asyncio.subprocess.PIPE,
177
+ cwd=self.workspace,
178
+ env=self.get_env()
179
+ )
180
+
181
+ try:
182
+ stdout, stderr = await asyncio.wait_for(
183
+ proc.communicate(),
184
+ timeout=timeout
185
+ )
186
+ return stdout.decode(), stderr.decode()
187
+ except asyncio.TimeoutError:
188
+ proc.kill()
189
+ await proc.wait()
190
+ raise TimeoutError(f"Command timed out after {timeout}s: {' '.join(cmd)}")
191
+
192
+ async def stream_async(
193
+ self,
194
+ cmd: List[str],
195
+ timeout: Optional[int] = None
196
+ ) -> AsyncIterator[str]:
197
+ """
198
+ Stream stdout lines from a command asynchronously.
199
+
200
+ Args:
201
+ cmd: Command and arguments as a list
202
+ timeout: Timeout in seconds for the entire operation
203
+
204
+ Yields:
205
+ str: Each line of output
206
+ """
207
+ timeout = timeout or self.timeout
208
+
209
+ proc = await asyncio.create_subprocess_exec(
210
+ *cmd,
211
+ stdout=asyncio.subprocess.PIPE,
212
+ stderr=asyncio.subprocess.PIPE,
213
+ cwd=self.workspace,
214
+ env=self.get_env()
215
+ )
216
+
217
+ try:
218
+ async def read_lines():
219
+ while True:
220
+ line = await proc.stdout.readline()
221
+ if not line:
222
+ break
223
+ yield line.decode().rstrip('\n')
224
+
225
+ async for line in read_lines():
226
+ yield line
227
+
228
+ except asyncio.TimeoutError:
229
+ proc.kill()
230
+ await proc.wait()
231
+ raise TimeoutError(f"Stream timed out after {timeout}s")
232
+ finally:
233
+ if proc.returncode is None:
234
+ proc.kill()
235
+ await proc.wait()
236
+
237
+ def as_tool(self) -> callable:
238
+ """
239
+ Return a callable suitable for use as a PraisonAI agent tool.
240
+
241
+ The returned function is synchronous and wraps the async execute method.
242
+ Uses asyncio.run() for proper event loop handling (Python 3.7+).
243
+
244
+ Returns:
245
+ callable: A function that can be used as an agent tool
246
+ """
247
+ # Capture self for closure
248
+ integration = self
249
+
250
+ def tool_func(query: str) -> str:
251
+ """Execute the CLI tool with the given query."""
252
+ # Use asyncio.run() for clean event loop management
253
+ # This creates a new event loop, runs the coroutine, and closes it
254
+ try:
255
+ # Check if we're already in an async context
256
+ asyncio.get_running_loop()
257
+ # If we're in an async context, use ThreadPoolExecutor to avoid nested loop
258
+ import concurrent.futures
259
+ with concurrent.futures.ThreadPoolExecutor() as executor:
260
+ future = executor.submit(asyncio.run, integration.execute(query))
261
+ return future.result()
262
+ except RuntimeError:
263
+ # No running loop, safe to use asyncio.run()
264
+ return asyncio.run(integration.execute(query))
265
+
266
+ # Set function metadata for agent tool registration
267
+ tool_func.__name__ = f"{self.cli_command}_tool"
268
+ tool_func.__doc__ = f"Execute {self.cli_command} for coding tasks."
269
+
270
+ return tool_func
271
+
272
+ def get_env(self) -> Dict[str, str]:
273
+ """
274
+ Get environment variables for CLI execution.
275
+
276
+ Subclasses can override this to add tool-specific environment variables.
277
+
278
+ Returns:
279
+ dict: Environment variables to pass to the CLI
280
+ """
281
+ return dict(os.environ)
282
+
283
+
284
+ def get_available_integrations() -> Dict[str, bool]:
285
+ """
286
+ Get a dictionary of all integrations and their availability status.
287
+
288
+ Returns:
289
+ dict: Mapping of integration name to availability (True/False)
290
+ """
291
+ from .claude_code import ClaudeCodeIntegration
292
+ from .gemini_cli import GeminiCLIIntegration
293
+ from .codex_cli import CodexCLIIntegration
294
+ from .cursor_cli import CursorCLIIntegration
295
+
296
+ integrations = {
297
+ 'claude': ClaudeCodeIntegration(),
298
+ 'gemini': GeminiCLIIntegration(),
299
+ 'codex': CodexCLIIntegration(),
300
+ 'cursor': CursorCLIIntegration(),
301
+ }
302
+
303
+ return {name: integration.is_available for name, integration in integrations.items()}
@@ -0,0 +1,270 @@
1
+ """
2
+ Claude Code CLI Integration.
3
+
4
+ Provides integration with Claude Code CLI for AI-powered coding tasks.
5
+
6
+ Features:
7
+ - Headless mode execution with JSON output
8
+ - Session continuation support
9
+ - System prompt customization
10
+ - Tool restrictions
11
+ - Optional SDK integration (when claude-agent-sdk is installed)
12
+
13
+ Usage:
14
+ from praisonai.integrations import ClaudeCodeIntegration
15
+
16
+ # Create integration
17
+ claude = ClaudeCodeIntegration(workspace="/path/to/project")
18
+
19
+ # Execute a coding task
20
+ result = await claude.execute("Refactor the auth module")
21
+
22
+ # Stream output
23
+ async for event in claude.stream("Add error handling"):
24
+ print(event)
25
+
26
+ # Use as agent tool
27
+ tool = claude.as_tool()
28
+ """
29
+
30
+ import json
31
+ import os
32
+ from typing import AsyncIterator, Dict, Any, Optional, List
33
+
34
+ from .base import BaseCLIIntegration
35
+
36
+
37
+ # Check if Claude Code SDK is available
38
+ CLAUDE_SDK_AVAILABLE = False
39
+ try:
40
+ from claude_agent_sdk import query as claude_query, ClaudeAgentOptions
41
+ CLAUDE_SDK_AVAILABLE = True
42
+ except ImportError:
43
+ pass
44
+
45
+
46
+ class ClaudeCodeIntegration(BaseCLIIntegration):
47
+ """
48
+ Integration with Claude Code CLI.
49
+
50
+ Supports both subprocess-based execution and SDK-based execution
51
+ (when claude-agent-sdk is installed).
52
+
53
+ Attributes:
54
+ output_format: Output format ("json", "text", "stream-json")
55
+ skip_permissions: Whether to skip permission prompts
56
+ system_prompt: Custom system prompt to append
57
+ allowed_tools: List of allowed tools (e.g., ["Read", "Write", "Bash"])
58
+ disallowed_tools: List of disallowed tools
59
+ use_sdk: Whether to use the SDK instead of subprocess
60
+ """
61
+
62
+ def __init__(
63
+ self,
64
+ workspace: str = ".",
65
+ timeout: int = 300,
66
+ output_format: str = "json",
67
+ skip_permissions: bool = True,
68
+ system_prompt: Optional[str] = None,
69
+ allowed_tools: Optional[List[str]] = None,
70
+ disallowed_tools: Optional[List[str]] = None,
71
+ use_sdk: bool = False,
72
+ model: Optional[str] = None,
73
+ ):
74
+ """
75
+ Initialize Claude Code integration.
76
+
77
+ Args:
78
+ workspace: Working directory for CLI execution
79
+ timeout: Timeout in seconds for CLI execution
80
+ output_format: Output format ("json", "text", "stream-json")
81
+ skip_permissions: Whether to skip permission prompts (--dangerously-skip-permissions)
82
+ system_prompt: Custom system prompt to append
83
+ allowed_tools: List of allowed tools
84
+ disallowed_tools: List of disallowed tools
85
+ use_sdk: Whether to use the SDK instead of subprocess
86
+ model: Model to use (e.g., "sonnet", "opus")
87
+ """
88
+ super().__init__(workspace=workspace, timeout=timeout)
89
+
90
+ self.output_format = output_format
91
+ self.skip_permissions = skip_permissions
92
+ self.system_prompt = system_prompt
93
+ self.allowed_tools = allowed_tools
94
+ self.disallowed_tools = disallowed_tools
95
+ self.use_sdk = use_sdk and CLAUDE_SDK_AVAILABLE
96
+ self.model = model
97
+
98
+ # Session management
99
+ self._session_active = False
100
+
101
+ @property
102
+ def cli_command(self) -> str:
103
+ """Return the CLI command name."""
104
+ return "claude"
105
+
106
+ @property
107
+ def sdk_available(self) -> bool:
108
+ """Check if the Claude Code SDK is available."""
109
+ return CLAUDE_SDK_AVAILABLE
110
+
111
+ def _build_command(
112
+ self,
113
+ prompt: str,
114
+ continue_session: bool = False,
115
+ **options
116
+ ) -> List[str]:
117
+ """
118
+ Build the Claude CLI command.
119
+
120
+ Args:
121
+ prompt: The prompt to send
122
+ continue_session: Whether to continue a previous session
123
+ **options: Additional options
124
+
125
+ Returns:
126
+ List of command arguments
127
+ """
128
+ cmd = ["claude"]
129
+
130
+ # Add print mode flag for non-interactive output
131
+ cmd.append("-p")
132
+
133
+ # Add output format
134
+ cmd.extend(["--output-format", self.output_format])
135
+
136
+ # Add continue flag if needed
137
+ if continue_session or self._session_active:
138
+ cmd.append("--continue")
139
+
140
+ # Add model if specified
141
+ if self.model:
142
+ cmd.extend(["--model", self.model])
143
+
144
+ # Add system prompt if specified
145
+ if self.system_prompt:
146
+ cmd.extend(["--append-system-prompt", self.system_prompt])
147
+
148
+ # Add allowed tools if specified
149
+ if self.allowed_tools:
150
+ cmd.extend(["--allowedTools", ",".join(self.allowed_tools)])
151
+
152
+ # Add disallowed tools if specified
153
+ if self.disallowed_tools:
154
+ cmd.extend(["--disallowedTools", ",".join(self.disallowed_tools)])
155
+
156
+ # Add verbose if needed
157
+ if options.get('verbose'):
158
+ cmd.append("--verbose")
159
+
160
+ # Add prompt last
161
+ cmd.append(prompt)
162
+
163
+ return cmd
164
+
165
+ async def execute(self, prompt: str, **options) -> str:
166
+ """
167
+ Execute Claude Code CLI and return the result.
168
+
169
+ Args:
170
+ prompt: The prompt/query to send
171
+ **options: Additional options (continue_session, etc.)
172
+
173
+ Returns:
174
+ str: The CLI output (parsed from JSON if output_format is "json")
175
+ """
176
+ if self.use_sdk:
177
+ return await self._execute_sdk(prompt, **options)
178
+
179
+ return await self._execute_subprocess(prompt, **options)
180
+
181
+ async def _execute_subprocess(self, prompt: str, **options) -> str:
182
+ """Execute using subprocess."""
183
+ cmd = self._build_command(prompt, **options)
184
+
185
+ output = await self.execute_async(cmd)
186
+
187
+ # Mark session as active for continuation
188
+ self._session_active = True
189
+
190
+ # Parse JSON output if applicable
191
+ if self.output_format == "json":
192
+ try:
193
+ data = json.loads(output)
194
+ # Extract the main result
195
+ if isinstance(data, dict):
196
+ return data.get("result", data.get("content", str(data)))
197
+ return str(data)
198
+ except json.JSONDecodeError:
199
+ return output
200
+
201
+ return output
202
+
203
+ async def _execute_sdk(self, prompt: str, **options) -> str:
204
+ """Execute using the Claude Code SDK."""
205
+ if not CLAUDE_SDK_AVAILABLE:
206
+ raise RuntimeError("Claude Code SDK not available. Install with: pip install claude-agent-sdk")
207
+
208
+ sdk_options = ClaudeAgentOptions(
209
+ cwd=self.workspace,
210
+ system_prompt=self.system_prompt,
211
+ )
212
+
213
+ if self.allowed_tools:
214
+ sdk_options.allowed_tools = self.allowed_tools
215
+
216
+ if self.skip_permissions:
217
+ sdk_options.permission_mode = 'acceptEdits'
218
+
219
+ result_parts = []
220
+ async for message in claude_query(prompt=prompt, options=sdk_options):
221
+ if hasattr(message, 'content'):
222
+ for block in message.content:
223
+ if hasattr(block, 'text'):
224
+ result_parts.append(block.text)
225
+
226
+ return '\n'.join(result_parts)
227
+
228
+ async def stream(self, prompt: str, **options) -> AsyncIterator[Dict[str, Any]]:
229
+ """
230
+ Stream output from Claude Code CLI.
231
+
232
+ Args:
233
+ prompt: The prompt/query to send
234
+ **options: Additional options
235
+
236
+ Yields:
237
+ dict: Parsed JSON events from the CLI
238
+ """
239
+ # Use stream-json format for streaming
240
+ original_format = self.output_format
241
+ self.output_format = "stream-json"
242
+
243
+ try:
244
+ cmd = self._build_command(prompt, **options)
245
+
246
+ async for line in self.stream_async(cmd):
247
+ if line.strip():
248
+ try:
249
+ event = json.loads(line)
250
+ yield event
251
+ except json.JSONDecodeError:
252
+ yield {"type": "text", "content": line}
253
+ finally:
254
+ self.output_format = original_format
255
+
256
+ def reset_session(self):
257
+ """Reset the session state."""
258
+ self._session_active = False
259
+
260
+ def get_env(self) -> Dict[str, str]:
261
+ """Get environment variables for CLI execution."""
262
+ env = super().get_env()
263
+
264
+ # Add Anthropic API key if available
265
+ if "ANTHROPIC_API_KEY" in os.environ:
266
+ env["ANTHROPIC_API_KEY"] = os.environ["ANTHROPIC_API_KEY"]
267
+ elif "CLAUDE_API_KEY" in os.environ:
268
+ env["ANTHROPIC_API_KEY"] = os.environ["CLAUDE_API_KEY"]
269
+
270
+ return env