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,603 @@
1
+ """
2
+ Interactive TUI System for PraisonAI CLI.
3
+
4
+ Inspired by Aider's interactive mode and prompt_toolkit usage.
5
+ Provides rich interactive terminal experience with completions and history.
6
+
7
+ Architecture:
8
+ - InteractiveSession: Main interactive session manager
9
+ - CommandCompleter: Auto-completion for commands and files
10
+ - HistoryManager: Persistent command history
11
+ - StatusDisplay: Rich-based status bar and output
12
+ """
13
+
14
+ from dataclasses import dataclass
15
+ from typing import Any, Callable, Dict, List, Optional
16
+ from pathlib import Path
17
+ import logging
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+
22
+ # ============================================================================
23
+ # Configuration
24
+ # ============================================================================
25
+
26
+ @dataclass
27
+ class InteractiveConfig:
28
+ """Configuration for interactive session."""
29
+ prompt: str = ">>> "
30
+ multiline: bool = True
31
+ history_file: Optional[str] = None
32
+ max_history: int = 1000
33
+ enable_completions: bool = True
34
+ enable_syntax_highlighting: bool = True
35
+ vi_mode: bool = False
36
+ auto_suggest: bool = True
37
+ show_status_bar: bool = True
38
+ color_scheme: str = "monokai"
39
+
40
+
41
+ # ============================================================================
42
+ # Command Completer
43
+ # ============================================================================
44
+
45
+ class CommandCompleter:
46
+ """
47
+ Provides auto-completion for commands, files, and symbols.
48
+ """
49
+
50
+ def __init__(
51
+ self,
52
+ commands: Optional[List[str]] = None,
53
+ file_patterns: Optional[List[str]] = None
54
+ ):
55
+ self.commands = commands or []
56
+ self.file_patterns = file_patterns or ["*.py", "*.js", "*.ts"]
57
+ self._file_cache: List[str] = []
58
+ self._symbol_cache: List[str] = []
59
+
60
+ def add_commands(self, commands: List[str]) -> None:
61
+ """Add commands to completion list."""
62
+ self.commands.extend(commands)
63
+
64
+ def add_symbols(self, symbols: List[str]) -> None:
65
+ """Add symbols to completion list."""
66
+ self._symbol_cache.extend(symbols)
67
+
68
+ def refresh_files(self, root: Optional[Path] = None) -> None:
69
+ """Refresh file cache."""
70
+ root = root or Path.cwd()
71
+ self._file_cache = []
72
+
73
+ for pattern in self.file_patterns:
74
+ for file_path in root.rglob(pattern):
75
+ try:
76
+ rel_path = str(file_path.relative_to(root))
77
+ if not any(excl in rel_path for excl in ["__pycache__", "node_modules", ".git"]):
78
+ self._file_cache.append(rel_path)
79
+ except ValueError:
80
+ pass
81
+
82
+ def get_completions(self, text: str, cursor_pos: int) -> List[str]:
83
+ """
84
+ Get completions for the current input.
85
+
86
+ Args:
87
+ text: Current input text
88
+ cursor_pos: Cursor position
89
+
90
+ Returns:
91
+ List of completion suggestions
92
+ """
93
+ # Get word before cursor
94
+ before_cursor = text[:cursor_pos]
95
+ words = before_cursor.split()
96
+ current_word = words[-1] if words else ""
97
+
98
+ completions = []
99
+
100
+ # Slash commands
101
+ if current_word.startswith("/"):
102
+ cmd_part = current_word[1:].lower()
103
+ for cmd in self.commands:
104
+ if cmd.lower().startswith(cmd_part):
105
+ completions.append(f"/{cmd}")
106
+
107
+ # @ mentions for files
108
+ elif current_word.startswith("@"):
109
+ file_part = current_word[1:].lower()
110
+ for file_path in self._file_cache:
111
+ if file_part in file_path.lower():
112
+ completions.append(f"@{file_path}")
113
+
114
+ # General completions
115
+ else:
116
+ word_lower = current_word.lower()
117
+
118
+ # Check symbols
119
+ for symbol in self._symbol_cache:
120
+ if symbol.lower().startswith(word_lower):
121
+ completions.append(symbol)
122
+
123
+ # Check files
124
+ for file_path in self._file_cache:
125
+ if word_lower in file_path.lower():
126
+ completions.append(file_path)
127
+
128
+ return completions[:20] # Limit results
129
+
130
+ def create_prompt_toolkit_completer(self) -> Any:
131
+ """Create a prompt_toolkit completer."""
132
+ try:
133
+ from prompt_toolkit.completion import Completer, Completion
134
+
135
+ outer_self = self
136
+
137
+ class PTCompleter(Completer):
138
+ def get_completions(self, document, complete_event):
139
+ text = document.text
140
+ cursor_pos = document.cursor_position
141
+
142
+ for completion in outer_self.get_completions(text, cursor_pos):
143
+ # Calculate start position
144
+ words = text[:cursor_pos].split()
145
+ current_word = words[-1] if words else ""
146
+ start_pos = -len(current_word)
147
+
148
+ yield Completion(completion, start_position=start_pos)
149
+
150
+ return PTCompleter()
151
+ except ImportError:
152
+ return None
153
+
154
+
155
+ # ============================================================================
156
+ # History Manager
157
+ # ============================================================================
158
+
159
+ class HistoryManager:
160
+ """
161
+ Manages command history with persistence.
162
+ """
163
+
164
+ def __init__(
165
+ self,
166
+ history_file: Optional[str] = None,
167
+ max_entries: int = 1000
168
+ ):
169
+ self.history_file = Path(history_file) if history_file else None
170
+ self.max_entries = max_entries
171
+ self._history: List[str] = []
172
+ self._position: int = 0
173
+
174
+ if self.history_file:
175
+ self._load_history()
176
+
177
+ def _load_history(self) -> None:
178
+ """Load history from file."""
179
+ if self.history_file and self.history_file.exists():
180
+ try:
181
+ with open(self.history_file, 'r', encoding='utf-8') as f:
182
+ self._history = [
183
+ line.strip() for line in f.readlines()
184
+ if line.strip()
185
+ ][-self.max_entries:]
186
+ except Exception as e:
187
+ logger.debug(f"Could not load history: {e}")
188
+
189
+ def _save_history(self) -> None:
190
+ """Save history to file."""
191
+ if self.history_file:
192
+ try:
193
+ self.history_file.parent.mkdir(parents=True, exist_ok=True)
194
+ with open(self.history_file, 'w', encoding='utf-8') as f:
195
+ for entry in self._history[-self.max_entries:]:
196
+ f.write(entry + "\n")
197
+ except Exception as e:
198
+ logger.debug(f"Could not save history: {e}")
199
+
200
+ def add(self, entry: str) -> None:
201
+ """Add an entry to history."""
202
+ entry = entry.strip()
203
+ if entry and (not self._history or self._history[-1] != entry):
204
+ self._history.append(entry)
205
+ self._position = len(self._history)
206
+ self._save_history()
207
+
208
+ def get_previous(self) -> Optional[str]:
209
+ """Get previous history entry."""
210
+ if self._position > 0:
211
+ self._position -= 1
212
+ return self._history[self._position]
213
+ return None
214
+
215
+ def get_next(self) -> Optional[str]:
216
+ """Get next history entry."""
217
+ if self._position < len(self._history) - 1:
218
+ self._position += 1
219
+ return self._history[self._position]
220
+ elif self._position == len(self._history) - 1:
221
+ self._position = len(self._history)
222
+ return ""
223
+ return None
224
+
225
+ def search(self, prefix: str) -> List[str]:
226
+ """Search history for entries starting with prefix."""
227
+ return [
228
+ entry for entry in self._history
229
+ if entry.lower().startswith(prefix.lower())
230
+ ]
231
+
232
+ def get_all(self) -> List[str]:
233
+ """Get all history entries."""
234
+ return self._history.copy()
235
+
236
+ def clear(self) -> None:
237
+ """Clear history."""
238
+ self._history.clear()
239
+ self._position = 0
240
+ if self.history_file and self.history_file.exists():
241
+ self.history_file.unlink()
242
+
243
+ def create_prompt_toolkit_history(self) -> Any:
244
+ """Create a prompt_toolkit history object."""
245
+ try:
246
+ from prompt_toolkit.history import FileHistory, InMemoryHistory
247
+
248
+ if self.history_file:
249
+ return FileHistory(str(self.history_file))
250
+ return InMemoryHistory()
251
+ except ImportError:
252
+ return None
253
+
254
+
255
+ # ============================================================================
256
+ # Status Display
257
+ # ============================================================================
258
+
259
+ class StatusDisplay:
260
+ """
261
+ Rich-based status display for interactive session.
262
+ """
263
+
264
+ def __init__(self, show_status_bar: bool = True):
265
+ self.show_status_bar = show_status_bar
266
+ self._console = None
267
+ self._status_items: Dict[str, str] = {}
268
+
269
+ @property
270
+ def console(self):
271
+ """Lazy load Rich console."""
272
+ if self._console is None:
273
+ try:
274
+ from rich.console import Console
275
+ self._console = Console()
276
+ except ImportError:
277
+ self._console = None
278
+ return self._console
279
+
280
+ def set_status(self, key: str, value: str) -> None:
281
+ """Set a status item."""
282
+ self._status_items[key] = value
283
+
284
+ def clear_status(self, key: str) -> None:
285
+ """Clear a status item."""
286
+ self._status_items.pop(key, None)
287
+
288
+ def print_status_bar(self) -> None:
289
+ """Print the status bar."""
290
+ if not self.show_status_bar or not self.console:
291
+ return
292
+
293
+ from rich.columns import Columns
294
+ from rich.text import Text
295
+
296
+ items = []
297
+ for key, value in self._status_items.items():
298
+ items.append(Text(f"{key}: {value}", style="dim"))
299
+
300
+ if items:
301
+ self.console.print(Columns(items, equal=True, expand=True))
302
+
303
+ def print_welcome(self, version: str = "1.0.0") -> None:
304
+ """Print welcome message."""
305
+ if not self.console:
306
+ print(f"PraisonAI CLI v{version}")
307
+ return
308
+
309
+ from rich.panel import Panel
310
+ from rich.text import Text
311
+
312
+ welcome = Text()
313
+ welcome.append("PraisonAI CLI", style="bold cyan")
314
+ welcome.append(f" v{version}\n", style="dim")
315
+ welcome.append("Type ", style="dim")
316
+ welcome.append("/help", style="bold green")
317
+ welcome.append(" for commands, ", style="dim")
318
+ welcome.append("/exit", style="bold yellow")
319
+ welcome.append(" to quit", style="dim")
320
+
321
+ self.console.print(Panel(welcome, border_style="blue"))
322
+
323
+ def print_response(self, response: str, title: str = "Response") -> None:
324
+ """Print a response with formatting."""
325
+ if not self.console:
326
+ print(response)
327
+ return
328
+
329
+ from rich.panel import Panel
330
+ from rich.markdown import Markdown
331
+
332
+ try:
333
+ md = Markdown(response)
334
+ self.console.print(Panel(md, title=title, border_style="green"))
335
+ except Exception:
336
+ self.console.print(Panel(response, title=title, border_style="green"))
337
+
338
+ def print_error(self, error: str) -> None:
339
+ """Print an error message."""
340
+ if not self.console:
341
+ print(f"Error: {error}")
342
+ return
343
+
344
+ from rich.panel import Panel
345
+ self.console.print(Panel(error, title="Error", border_style="red"))
346
+
347
+ def print_info(self, message: str) -> None:
348
+ """Print an info message."""
349
+ if not self.console:
350
+ print(message)
351
+ return
352
+
353
+ self.console.print(f"[cyan]ℹ[/cyan] {message}")
354
+
355
+ def print_success(self, message: str) -> None:
356
+ """Print a success message."""
357
+ if not self.console:
358
+ print(message)
359
+ return
360
+
361
+ self.console.print(f"[green]✓[/green] {message}")
362
+
363
+
364
+ # ============================================================================
365
+ # Interactive Session
366
+ # ============================================================================
367
+
368
+ class InteractiveSession:
369
+ """
370
+ Main interactive session manager.
371
+
372
+ Provides a rich interactive terminal experience with:
373
+ - Command completion
374
+ - History
375
+ - Syntax highlighting
376
+ - Status bar
377
+ - Keyboard shortcuts
378
+ """
379
+
380
+ def __init__(
381
+ self,
382
+ config: Optional[InteractiveConfig] = None,
383
+ on_input: Optional[Callable[[str], Optional[str]]] = None,
384
+ on_command: Optional[Callable[[str], Optional[Dict[str, Any]]]] = None
385
+ ):
386
+ self.config = config or InteractiveConfig()
387
+ self.on_input = on_input
388
+ self.on_command = on_command
389
+
390
+ self.completer = CommandCompleter()
391
+ self.history = HistoryManager(
392
+ history_file=self.config.history_file,
393
+ max_entries=self.config.max_history
394
+ )
395
+ self.display = StatusDisplay(show_status_bar=self.config.show_status_bar)
396
+
397
+ self._running = False
398
+ self._prompt_session = None
399
+
400
+ def _create_prompt_session(self) -> Any:
401
+ """Create a prompt_toolkit session."""
402
+ try:
403
+ from prompt_toolkit import PromptSession
404
+ from prompt_toolkit.lexers import PygmentsLexer
405
+ from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
406
+ from pygments.lexers import MarkdownLexer
407
+
408
+ session_kwargs = {
409
+ "message": self.config.prompt,
410
+ "multiline": self.config.multiline,
411
+ }
412
+
413
+ # Add completer
414
+ if self.config.enable_completions:
415
+ completer = self.completer.create_prompt_toolkit_completer()
416
+ if completer:
417
+ session_kwargs["completer"] = completer
418
+
419
+ # Add history
420
+ history = self.history.create_prompt_toolkit_history()
421
+ if history:
422
+ session_kwargs["history"] = history
423
+
424
+ # Add syntax highlighting
425
+ if self.config.enable_syntax_highlighting:
426
+ session_kwargs["lexer"] = PygmentsLexer(MarkdownLexer)
427
+
428
+ # Add auto-suggest
429
+ if self.config.auto_suggest:
430
+ session_kwargs["auto_suggest"] = AutoSuggestFromHistory()
431
+
432
+ # VI mode
433
+ if self.config.vi_mode:
434
+ from prompt_toolkit.enums import EditingMode
435
+ session_kwargs["editing_mode"] = EditingMode.VI
436
+
437
+ return PromptSession(**session_kwargs)
438
+
439
+ except ImportError as e:
440
+ logger.debug(f"prompt_toolkit not available: {e}")
441
+ return None
442
+
443
+ def _get_input_fallback(self) -> str:
444
+ """Fallback input method without prompt_toolkit."""
445
+ try:
446
+ if self.config.multiline:
447
+ print(f"{self.config.prompt}(Enter empty line to submit)")
448
+ lines = []
449
+ while True:
450
+ line = input()
451
+ if not line:
452
+ break
453
+ lines.append(line)
454
+ return "\n".join(lines)
455
+ else:
456
+ return input(self.config.prompt)
457
+ except EOFError:
458
+ return "/exit"
459
+ except KeyboardInterrupt:
460
+ print()
461
+ return ""
462
+
463
+ def get_input(self) -> str:
464
+ """Get input from user."""
465
+ if self._prompt_session:
466
+ try:
467
+ return self._prompt_session.prompt()
468
+ except KeyboardInterrupt:
469
+ return ""
470
+ except EOFError:
471
+ return "/exit"
472
+ else:
473
+ return self._get_input_fallback()
474
+
475
+ def process_input(self, user_input: str) -> Optional[str]:
476
+ """
477
+ Process user input.
478
+
479
+ Args:
480
+ user_input: Raw user input
481
+
482
+ Returns:
483
+ Response string or None
484
+ """
485
+ user_input = user_input.strip()
486
+
487
+ if not user_input:
488
+ return None
489
+
490
+ # Add to history
491
+ self.history.add(user_input)
492
+
493
+ # Check for slash commands
494
+ if user_input.startswith("/"):
495
+ if self.on_command:
496
+ result = self.on_command(user_input)
497
+ if result:
498
+ if result.get("type") == "exit":
499
+ self._running = False
500
+ return None
501
+ return result.get("message")
502
+ return None
503
+
504
+ # Regular input
505
+ if self.on_input:
506
+ return self.on_input(user_input)
507
+
508
+ return None
509
+
510
+ def run(self) -> None:
511
+ """Run the interactive session."""
512
+ self._running = True
513
+ self._prompt_session = self._create_prompt_session()
514
+
515
+ # Print welcome
516
+ self.display.print_welcome()
517
+
518
+ while self._running:
519
+ try:
520
+ # Get input
521
+ user_input = self.get_input()
522
+
523
+ # Process
524
+ response = self.process_input(user_input)
525
+
526
+ # Display response
527
+ if response:
528
+ self.display.print_response(response)
529
+
530
+ except KeyboardInterrupt:
531
+ self.display.print_info("Use /exit to quit")
532
+ except Exception as e:
533
+ self.display.print_error(str(e))
534
+ logger.exception("Error in interactive session")
535
+
536
+ def stop(self) -> None:
537
+ """Stop the interactive session."""
538
+ self._running = False
539
+
540
+ def add_commands(self, commands: List[str]) -> None:
541
+ """Add commands for completion."""
542
+ self.completer.add_commands(commands)
543
+
544
+ def add_symbols(self, symbols: List[str]) -> None:
545
+ """Add symbols for completion."""
546
+ self.completer.add_symbols(symbols)
547
+
548
+ def refresh_files(self, root: Optional[Path] = None) -> None:
549
+ """Refresh file completions."""
550
+ self.completer.refresh_files(root)
551
+
552
+
553
+ # ============================================================================
554
+ # CLI Integration Handler
555
+ # ============================================================================
556
+
557
+ class InteractiveTUIHandler:
558
+ """
559
+ Handler for integrating Interactive TUI with PraisonAI CLI.
560
+ """
561
+
562
+ def __init__(self, verbose: bool = False):
563
+ self.verbose = verbose
564
+ self._session: Optional[InteractiveSession] = None
565
+
566
+ @property
567
+ def feature_name(self) -> str:
568
+ return "interactive_tui"
569
+
570
+ def initialize(
571
+ self,
572
+ config: Optional[InteractiveConfig] = None,
573
+ on_input: Optional[Callable[[str], Optional[str]]] = None,
574
+ on_command: Optional[Callable[[str], Optional[Dict[str, Any]]]] = None
575
+ ) -> InteractiveSession:
576
+ """Initialize the interactive session."""
577
+ self._session = InteractiveSession(
578
+ config=config,
579
+ on_input=on_input,
580
+ on_command=on_command
581
+ )
582
+
583
+ if self.verbose:
584
+ from rich import print as rprint
585
+ rprint("[cyan]Interactive TUI initialized[/cyan]")
586
+
587
+ return self._session
588
+
589
+ def get_session(self) -> Optional[InteractiveSession]:
590
+ """Get the current session."""
591
+ return self._session
592
+
593
+ def run(self) -> None:
594
+ """Run the interactive session."""
595
+ if not self._session:
596
+ self._session = self.initialize()
597
+
598
+ self._session.run()
599
+
600
+ def stop(self) -> None:
601
+ """Stop the interactive session."""
602
+ if self._session:
603
+ self._session.stop()