praisonai-code 0.0.1__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 (309) hide show
  1. praisonai_code/__init__.py +17 -0
  2. praisonai_code/cli/__init__.py +12 -0
  3. praisonai_code/cli/_forward_shim.py +10 -0
  4. praisonai_code/cli/_paths.py +88 -0
  5. praisonai_code/cli/_warnings.py +58 -0
  6. praisonai_code/cli/app.py +757 -0
  7. praisonai_code/cli/approval_backend.py +272 -0
  8. praisonai_code/cli/branding.py +94 -0
  9. praisonai_code/cli/commands/__init__.py +114 -0
  10. praisonai_code/cli/commands/acp.py +80 -0
  11. praisonai_code/cli/commands/agent.py +116 -0
  12. praisonai_code/cli/commands/agents.py +80 -0
  13. praisonai_code/cli/commands/app.py +139 -0
  14. praisonai_code/cli/commands/attach.py +95 -0
  15. praisonai_code/cli/commands/audit.py +102 -0
  16. praisonai_code/cli/commands/auth.py +508 -0
  17. praisonai_code/cli/commands/batch.py +848 -0
  18. praisonai_code/cli/commands/benchmark.py +286 -0
  19. praisonai_code/cli/commands/browser.py +299 -0
  20. praisonai_code/cli/commands/call.py +45 -0
  21. praisonai_code/cli/commands/chat.py +332 -0
  22. praisonai_code/cli/commands/checkpoint.py +170 -0
  23. praisonai_code/cli/commands/code.py +276 -0
  24. praisonai_code/cli/commands/command.py +114 -0
  25. praisonai_code/cli/commands/commit.py +47 -0
  26. praisonai_code/cli/commands/completion.py +333 -0
  27. praisonai_code/cli/commands/config.py +681 -0
  28. praisonai_code/cli/commands/context.py +414 -0
  29. praisonai_code/cli/commands/daemon.py +203 -0
  30. praisonai_code/cli/commands/debug.py +142 -0
  31. praisonai_code/cli/commands/deploy.py +71 -0
  32. praisonai_code/cli/commands/diag.py +55 -0
  33. praisonai_code/cli/commands/docs.py +1575 -0
  34. praisonai_code/cli/commands/doctor.py +332 -0
  35. praisonai_code/cli/commands/endpoints.py +51 -0
  36. praisonai_code/cli/commands/environment.py +179 -0
  37. praisonai_code/cli/commands/eval.py +131 -0
  38. praisonai_code/cli/commands/examples.py +953 -0
  39. praisonai_code/cli/commands/flow.py +436 -0
  40. praisonai_code/cli/commands/github.py +752 -0
  41. praisonai_code/cli/commands/hooks.py +74 -0
  42. praisonai_code/cli/commands/init.py +174 -0
  43. praisonai_code/cli/commands/knowledge.py +440 -0
  44. praisonai_code/cli/commands/langextract.py +120 -0
  45. praisonai_code/cli/commands/langfuse.py +984 -0
  46. praisonai_code/cli/commands/loop.py +211 -0
  47. praisonai_code/cli/commands/lsp.py +112 -0
  48. praisonai_code/cli/commands/managed.py +659 -0
  49. praisonai_code/cli/commands/mcp.py +763 -0
  50. praisonai_code/cli/commands/memory.py +298 -0
  51. praisonai_code/cli/commands/models.py +264 -0
  52. praisonai_code/cli/commands/n8n.py +326 -0
  53. praisonai_code/cli/commands/obs.py +19 -0
  54. praisonai_code/cli/commands/package.py +76 -0
  55. praisonai_code/cli/commands/paths.py +106 -0
  56. praisonai_code/cli/commands/permissions.py +272 -0
  57. praisonai_code/cli/commands/plugins.py +609 -0
  58. praisonai_code/cli/commands/port.py +530 -0
  59. praisonai_code/cli/commands/profile.py +466 -0
  60. praisonai_code/cli/commands/publish.py +193 -0
  61. praisonai_code/cli/commands/rag.py +913 -0
  62. praisonai_code/cli/commands/realtime.py +52 -0
  63. praisonai_code/cli/commands/recipe.py +684 -0
  64. praisonai_code/cli/commands/registry.py +59 -0
  65. praisonai_code/cli/commands/replay.py +830 -0
  66. praisonai_code/cli/commands/research.py +49 -0
  67. praisonai_code/cli/commands/retrieval.py +377 -0
  68. praisonai_code/cli/commands/rules.py +71 -0
  69. praisonai_code/cli/commands/run.py +1573 -0
  70. praisonai_code/cli/commands/sandbox.py +371 -0
  71. praisonai_code/cli/commands/schedule.py +529 -0
  72. praisonai_code/cli/commands/serve.py +690 -0
  73. praisonai_code/cli/commands/session.py +450 -0
  74. praisonai_code/cli/commands/setup.py +174 -0
  75. praisonai_code/cli/commands/skills.py +545 -0
  76. praisonai_code/cli/commands/standardise.py +711 -0
  77. praisonai_code/cli/commands/templates.py +54 -0
  78. praisonai_code/cli/commands/test.py +558 -0
  79. praisonai_code/cli/commands/todo.py +74 -0
  80. praisonai_code/cli/commands/tools.py +205 -0
  81. praisonai_code/cli/commands/traces.py +145 -0
  82. praisonai_code/cli/commands/tracker.py +852 -0
  83. praisonai_code/cli/commands/train.py +613 -0
  84. praisonai_code/cli/commands/ui.py +172 -0
  85. praisonai_code/cli/commands/up.py +354 -0
  86. praisonai_code/cli/commands/validate.py +291 -0
  87. praisonai_code/cli/commands/version.py +101 -0
  88. praisonai_code/cli/commands/workflow.py +97 -0
  89. praisonai_code/cli/config_loader.py +437 -0
  90. praisonai_code/cli/configuration/__init__.py +27 -0
  91. praisonai_code/cli/configuration/config.schema.json +57 -0
  92. praisonai_code/cli/configuration/credentials.py +446 -0
  93. praisonai_code/cli/configuration/loader.py +364 -0
  94. praisonai_code/cli/configuration/model_resolver.py +161 -0
  95. praisonai_code/cli/configuration/oauth.py +389 -0
  96. praisonai_code/cli/configuration/paths.py +224 -0
  97. praisonai_code/cli/configuration/resolver.py +687 -0
  98. praisonai_code/cli/configuration/schema.py +317 -0
  99. praisonai_code/cli/execution/__init__.py +99 -0
  100. praisonai_code/cli/execution/core.py +208 -0
  101. praisonai_code/cli/execution/profiler.py +898 -0
  102. praisonai_code/cli/execution/request.py +85 -0
  103. praisonai_code/cli/execution/result.py +74 -0
  104. praisonai_code/cli/fallback_schema.py +416 -0
  105. praisonai_code/cli/features/__init__.py +278 -0
  106. praisonai_code/cli/features/_endpoint_registry.py +64 -0
  107. praisonai_code/cli/features/_search_registry.py +43 -0
  108. praisonai_code/cli/features/acp.py +236 -0
  109. praisonai_code/cli/features/action_orchestrator.py +576 -0
  110. praisonai_code/cli/features/agent_scheduler.py +773 -0
  111. praisonai_code/cli/features/agent_tools.py +603 -0
  112. praisonai_code/cli/features/agents.py +397 -0
  113. praisonai_code/cli/features/at_mentions.py +471 -0
  114. praisonai_code/cli/features/audit_cli.py +270 -0
  115. praisonai_code/cli/features/auto_memory.py +182 -0
  116. praisonai_code/cli/features/auto_mode.py +552 -0
  117. praisonai_code/cli/features/autonomy_mode.py +546 -0
  118. praisonai_code/cli/features/background.py +356 -0
  119. praisonai_code/cli/features/base.py +168 -0
  120. praisonai_code/cli/features/benchmark.py +1462 -0
  121. praisonai_code/cli/features/capabilities.py +1326 -0
  122. praisonai_code/cli/features/checkpoints.py +345 -0
  123. praisonai_code/cli/features/cli_profiler.py +335 -0
  124. praisonai_code/cli/features/code_intelligence.py +666 -0
  125. praisonai_code/cli/features/compaction.py +294 -0
  126. praisonai_code/cli/features/compare.py +534 -0
  127. praisonai_code/cli/features/config_hierarchy.py +366 -0
  128. praisonai_code/cli/features/context_manager.py +597 -0
  129. praisonai_code/cli/features/cost_tracker.py +514 -0
  130. praisonai_code/cli/features/csv_test_runner.py +736 -0
  131. praisonai_code/cli/features/custom_definitions.py +790 -0
  132. praisonai_code/cli/features/debug.py +810 -0
  133. praisonai_code/cli/features/deploy.py +605 -0
  134. praisonai_code/cli/features/diag.py +289 -0
  135. praisonai_code/cli/features/display_jsonl.py +173 -0
  136. praisonai_code/cli/features/doctor/__init__.py +63 -0
  137. praisonai_code/cli/features/doctor/checks/__init__.py +29 -0
  138. praisonai_code/cli/features/doctor/checks/acp_checks.py +220 -0
  139. praisonai_code/cli/features/doctor/checks/bot_checks.py +340 -0
  140. praisonai_code/cli/features/doctor/checks/config_checks.py +373 -0
  141. praisonai_code/cli/features/doctor/checks/db_checks.py +366 -0
  142. praisonai_code/cli/features/doctor/checks/env_checks.py +637 -0
  143. praisonai_code/cli/features/doctor/checks/gateway_checks.py +387 -0
  144. praisonai_code/cli/features/doctor/checks/lsp_checks.py +231 -0
  145. praisonai_code/cli/features/doctor/checks/mcp_checks.py +367 -0
  146. praisonai_code/cli/features/doctor/checks/memory_checks.py +268 -0
  147. praisonai_code/cli/features/doctor/checks/network_checks.py +251 -0
  148. praisonai_code/cli/features/doctor/checks/obs_checks.py +328 -0
  149. praisonai_code/cli/features/doctor/checks/packaging_checks.py +422 -0
  150. praisonai_code/cli/features/doctor/checks/performance_checks.py +235 -0
  151. praisonai_code/cli/features/doctor/checks/permissions_checks.py +259 -0
  152. praisonai_code/cli/features/doctor/checks/runtime_checks.py +650 -0
  153. praisonai_code/cli/features/doctor/checks/runtime_migration_checks.py +220 -0
  154. praisonai_code/cli/features/doctor/checks/selftest_checks.py +322 -0
  155. praisonai_code/cli/features/doctor/checks/serve_checks.py +426 -0
  156. praisonai_code/cli/features/doctor/checks/skills_checks.py +327 -0
  157. praisonai_code/cli/features/doctor/checks/tools_checks.py +371 -0
  158. praisonai_code/cli/features/doctor/engine.py +266 -0
  159. praisonai_code/cli/features/doctor/formatters.py +377 -0
  160. praisonai_code/cli/features/doctor/handler.py +564 -0
  161. praisonai_code/cli/features/doctor/models.py +276 -0
  162. praisonai_code/cli/features/doctor/registry.py +239 -0
  163. praisonai_code/cli/features/endpoints.py +1016 -0
  164. praisonai_code/cli/features/eval.py +559 -0
  165. praisonai_code/cli/features/examples.py +707 -0
  166. praisonai_code/cli/features/external_agents.py +231 -0
  167. praisonai_code/cli/features/fast_context.py +410 -0
  168. praisonai_code/cli/features/file_history.py +320 -0
  169. praisonai_code/cli/features/flow_display.py +566 -0
  170. praisonai_code/cli/features/git_attribution.py +159 -0
  171. praisonai_code/cli/features/git_integration.py +651 -0
  172. praisonai_code/cli/features/guardrail.py +171 -0
  173. praisonai_code/cli/features/handoff.py +252 -0
  174. praisonai_code/cli/features/hooks.py +583 -0
  175. praisonai_code/cli/features/hybrid_workflow.py +391 -0
  176. praisonai_code/cli/features/image.py +384 -0
  177. praisonai_code/cli/features/interactive_core_headless.py +450 -0
  178. praisonai_code/cli/features/interactive_runtime.py +600 -0
  179. praisonai_code/cli/features/interactive_test_harness.py +537 -0
  180. praisonai_code/cli/features/interactive_tools.py +428 -0
  181. praisonai_code/cli/features/interactive_tui.py +603 -0
  182. praisonai_code/cli/features/job_workflow.py +906 -0
  183. praisonai_code/cli/features/jobs.py +632 -0
  184. praisonai_code/cli/features/knowledge.py +531 -0
  185. praisonai_code/cli/features/knowledge_cli.py +438 -0
  186. praisonai_code/cli/features/lite.py +244 -0
  187. praisonai_code/cli/features/logs.py +200 -0
  188. praisonai_code/cli/features/lsp_cli.py +225 -0
  189. praisonai_code/cli/features/lsp_diagnostics.py +185 -0
  190. praisonai_code/cli/features/mcp.py +344 -0
  191. praisonai_code/cli/features/message_queue.py +587 -0
  192. praisonai_code/cli/features/metrics.py +210 -0
  193. praisonai_code/cli/features/migrate.py +1329 -0
  194. praisonai_code/cli/features/migration_flow.py +463 -0
  195. praisonai_code/cli/features/migration_spec.py +276 -0
  196. praisonai_code/cli/features/n8n.py +703 -0
  197. praisonai_code/cli/features/observability.py +293 -0
  198. praisonai_code/cli/features/ollama.py +361 -0
  199. praisonai_code/cli/features/output_modes.py +155 -0
  200. praisonai_code/cli/features/output_style.py +273 -0
  201. praisonai_code/cli/features/package.py +631 -0
  202. praisonai_code/cli/features/performance.py +308 -0
  203. praisonai_code/cli/features/persistence.py +636 -0
  204. praisonai_code/cli/features/profiler/__init__.py +81 -0
  205. praisonai_code/cli/features/profiler/core.py +558 -0
  206. praisonai_code/cli/features/profiler/optimizations.py +652 -0
  207. praisonai_code/cli/features/profiler/suite.py +386 -0
  208. praisonai_code/cli/features/queue/__init__.py +73 -0
  209. praisonai_code/cli/features/queue/manager.py +435 -0
  210. praisonai_code/cli/features/queue/models.py +289 -0
  211. praisonai_code/cli/features/queue/persistence.py +564 -0
  212. praisonai_code/cli/features/queue/scheduler.py +529 -0
  213. praisonai_code/cli/features/queue/worker.py +400 -0
  214. praisonai_code/cli/features/recipe.py +2187 -0
  215. praisonai_code/cli/features/recipe_creator.py +996 -0
  216. praisonai_code/cli/features/recipe_optimizer.py +1364 -0
  217. praisonai_code/cli/features/recipe_prompts.py +226 -0
  218. praisonai_code/cli/features/registry.py +229 -0
  219. praisonai_code/cli/features/repo_map.py +860 -0
  220. praisonai_code/cli/features/router.py +466 -0
  221. praisonai_code/cli/features/safe_shell.py +427 -0
  222. praisonai_code/cli/features/sandbox_cli.py +283 -0
  223. praisonai_code/cli/features/sandbox_executor.py +536 -0
  224. praisonai_code/cli/features/sdk_knowledge.py +500 -0
  225. praisonai_code/cli/features/session.py +222 -0
  226. praisonai_code/cli/features/session_checkpoints.py +208 -0
  227. praisonai_code/cli/features/setup/__init__.py +9 -0
  228. praisonai_code/cli/features/setup/handler.py +355 -0
  229. praisonai_code/cli/features/setup/templates.py +62 -0
  230. praisonai_code/cli/features/skills.py +940 -0
  231. praisonai_code/cli/features/slash_commands.py +692 -0
  232. praisonai_code/cli/features/telemetry.py +179 -0
  233. praisonai_code/cli/features/templates.py +1390 -0
  234. praisonai_code/cli/features/thinking.py +343 -0
  235. praisonai_code/cli/features/todo.py +334 -0
  236. praisonai_code/cli/features/tools.py +680 -0
  237. praisonai_code/cli/features/tui/__init__.py +83 -0
  238. praisonai_code/cli/features/tui/app.py +871 -0
  239. praisonai_code/cli/features/tui/cli.py +580 -0
  240. praisonai_code/cli/features/tui/config.py +150 -0
  241. praisonai_code/cli/features/tui/debug.py +526 -0
  242. praisonai_code/cli/features/tui/events.py +99 -0
  243. praisonai_code/cli/features/tui/mock_provider.py +328 -0
  244. praisonai_code/cli/features/tui/orchestrator.py +652 -0
  245. praisonai_code/cli/features/tui/screens/__init__.py +50 -0
  246. praisonai_code/cli/features/tui/screens/help.py +157 -0
  247. praisonai_code/cli/features/tui/screens/main.py +568 -0
  248. praisonai_code/cli/features/tui/screens/queue.py +174 -0
  249. praisonai_code/cli/features/tui/screens/session.py +124 -0
  250. praisonai_code/cli/features/tui/screens/settings.py +148 -0
  251. praisonai_code/cli/features/tui/session_store.py +198 -0
  252. praisonai_code/cli/features/tui/widgets/__init__.py +56 -0
  253. praisonai_code/cli/features/tui/widgets/chat.py +263 -0
  254. praisonai_code/cli/features/tui/widgets/command_popup.py +258 -0
  255. praisonai_code/cli/features/tui/widgets/composer.py +292 -0
  256. praisonai_code/cli/features/tui/widgets/file_popup.py +207 -0
  257. praisonai_code/cli/features/tui/widgets/queue_panel.py +223 -0
  258. praisonai_code/cli/features/tui/widgets/status.py +181 -0
  259. praisonai_code/cli/features/tui/widgets/tool_panel.py +307 -0
  260. praisonai_code/cli/features/wizard.py +289 -0
  261. praisonai_code/cli/features/workflow.py +802 -0
  262. praisonai_code/cli/features/yaml_utils.py +321 -0
  263. praisonai_code/cli/interactive/__init__.py +48 -0
  264. praisonai_code/cli/interactive/async_tui.py +1218 -0
  265. praisonai_code/cli/interactive/config.py +139 -0
  266. praisonai_code/cli/interactive/core.py +618 -0
  267. praisonai_code/cli/interactive/events.py +131 -0
  268. praisonai_code/cli/interactive/frontends/__init__.py +31 -0
  269. praisonai_code/cli/interactive/frontends/rich_frontend.py +462 -0
  270. praisonai_code/cli/interactive/frontends/textual_frontend.py +157 -0
  271. praisonai_code/cli/interactive/praison_io.py +502 -0
  272. praisonai_code/cli/interactive/repl.py +297 -0
  273. praisonai_code/cli/interactive/split_tui.py +456 -0
  274. praisonai_code/cli/interactive/tui_app.py +457 -0
  275. praisonai_code/cli/langfuse_client.py +360 -0
  276. praisonai_code/cli/main.py +7421 -0
  277. praisonai_code/cli/output/__init__.py +25 -0
  278. praisonai_code/cli/output/console.py +456 -0
  279. praisonai_code/cli/output/event_bridge.py +191 -0
  280. praisonai_code/cli/schedule_cli.py +54 -0
  281. praisonai_code/cli/schema_provider.py +23 -0
  282. praisonai_code/cli/session/__init__.py +16 -0
  283. praisonai_code/cli/session/resume.py +148 -0
  284. praisonai_code/cli/session/unified.py +548 -0
  285. praisonai_code/cli/state/__init__.py +31 -0
  286. praisonai_code/cli/state/identifiers.py +161 -0
  287. praisonai_code/cli/state/project_sessions.py +383 -0
  288. praisonai_code/cli/state/sessions.py +390 -0
  289. praisonai_code/cli/ui/__init__.py +160 -0
  290. praisonai_code/cli/ui/config.py +46 -0
  291. praisonai_code/cli/ui/events.py +61 -0
  292. praisonai_code/cli/ui/mg_backend.py +342 -0
  293. praisonai_code/cli/ui/plain.py +133 -0
  294. praisonai_code/cli/ui/rich_backend.py +162 -0
  295. praisonai_code/cli/unified_schema.py +655 -0
  296. praisonai_code/cli/utils/env_utils.py +126 -0
  297. praisonai_code/cli/utils/project.py +131 -0
  298. praisonai_code/cli_backends/__init__.py +73 -0
  299. praisonai_code/cli_backends/claude.py +373 -0
  300. praisonai_code/cli_backends/registry.py +113 -0
  301. praisonai_code/runtime/__init__.py +36 -0
  302. praisonai_code/runtime/__main__.py +81 -0
  303. praisonai_code/runtime/client.py +131 -0
  304. praisonai_code/runtime/descriptor.py +209 -0
  305. praisonai_code/runtime/server.py +356 -0
  306. praisonai_code-0.0.1.dist-info/METADATA +80 -0
  307. praisonai_code-0.0.1.dist-info/RECORD +309 -0
  308. praisonai_code-0.0.1.dist-info/WHEEL +5 -0
  309. praisonai_code-0.0.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,757 @@
1
+ """
2
+ PraisonAI CLI Typer Application.
3
+
4
+ Main Typer app that registers all command groups and handles global options.
5
+ """
6
+
7
+ import importlib
8
+ from enum import Enum
9
+ from typing import Optional, Dict, Tuple, List
10
+
11
+ import typer
12
+ import click
13
+ from typer.core import TyperGroup
14
+ from typer.main import get_command as typer_get_command
15
+
16
+ from .output.console import OutputController, OutputMode, set_output_controller
17
+ from .state.identifiers import create_context
18
+
19
+
20
+ def _setup_langfuse_observability(*, verbose: bool = False) -> None:
21
+ """Set up Langfuse observability by wiring TraceSink to action emitter."""
22
+ try:
23
+ from praisonai.observability.langfuse import LangfuseSink
24
+ from praisonaiagents.trace.protocol import TraceEmitter, set_default_emitter
25
+ from praisonaiagents.trace.context_events import ContextTraceEmitter, set_context_emitter
26
+ import atexit
27
+
28
+ # Create LangfuseSink (auto-reads env vars)
29
+ sink = LangfuseSink()
30
+
31
+ # Set up action-level trace emitter (covers RouterAgent / PlanningAgent)
32
+ emitter = TraceEmitter(sink=sink, enabled=True)
33
+ set_default_emitter(emitter)
34
+
35
+ # Set up context-level trace emitter (captures Agent.start() lifecycle)
36
+ context_emitter = ContextTraceEmitter(sink=sink.context_sink(), enabled=True)
37
+ set_context_emitter(context_emitter)
38
+
39
+ # Register atexit close for the sink
40
+ atexit.register(sink.close)
41
+
42
+ except ImportError:
43
+ # Gracefully degrade if Langfuse not installed
44
+ pass
45
+ except Exception as e:
46
+ # Avoid breaking CLI if observability setup fails
47
+ if verbose:
48
+ typer.echo(f"Warning: failed to initialize Langfuse observability: {e}", err=True)
49
+
50
+
51
+ def _setup_langextract_observability(*, verbose: bool = False) -> None:
52
+ """Set up Langextract observability by wiring TraceSink to action emitter."""
53
+ try:
54
+ import importlib.util
55
+
56
+ # Explicitly check if langextract is available before attempting to use it
57
+ if importlib.util.find_spec('langextract') is None:
58
+ if verbose:
59
+ typer.echo("Warning: langextract is not installed. Install with: pip install 'praisonai[langextract]'", err=True)
60
+ return
61
+
62
+ from praisonai.observability.langextract import LangextractSink, LangextractSinkConfig
63
+ from praisonaiagents.trace.protocol import TraceEmitter, set_default_emitter
64
+ import os
65
+ import atexit
66
+
67
+ # Build LangextractSinkConfig from env vars
68
+ config = LangextractSinkConfig(
69
+ output_path=os.getenv("PRAISONAI_LANGEXTRACT_OUTPUT", "praisonai-trace.html"),
70
+ auto_open=os.getenv("PRAISONAI_LANGEXTRACT_AUTO_OPEN", "false").lower() == "true",
71
+ )
72
+
73
+ # Create LangextractSink
74
+ sink = LangextractSink(config=config)
75
+
76
+ # Ensure sink is closed on exit to write the trace file
77
+ atexit.register(sink.close)
78
+
79
+ # Set up action-level trace emitter (covers RouterAgent / PlanningAgent)
80
+ emitter = TraceEmitter(sink=sink, enabled=True)
81
+ set_default_emitter(emitter)
82
+
83
+ # Bridge the context emitter so regular Agent.start / tool calls / LLM
84
+ # responses are captured as well. Without this, typical single-agent
85
+ # flows produce an empty trace (no agent_start/end, no tool events).
86
+ def warn_handler(msg: str):
87
+ if verbose:
88
+ typer.echo(f"Warning: {msg}", err=True)
89
+
90
+ LangextractSink.bridge_context_events(
91
+ sink=sink,
92
+ session_id="praisonai-cli",
93
+ warn_callback=warn_handler
94
+ )
95
+
96
+ except ImportError:
97
+ # Gracefully degrade if langextract not installed
98
+ if verbose:
99
+ typer.echo("Warning: langextract is not installed. Install with: pip install 'praisonai[langextract]'", err=True)
100
+ except Exception as e:
101
+ # Avoid breaking CLI if observability setup fails
102
+ if verbose:
103
+ typer.echo(f"Warning: failed to initialize langextract observability: {e}", err=True)
104
+
105
+
106
+ class OutputFormat(str, Enum):
107
+ """Output format options."""
108
+ text = "text"
109
+ json = "json"
110
+ stream_json = "stream-json"
111
+
112
+
113
+ # Command registry for lazy loading
114
+ _LAZY_COMMANDS: Dict[str, Tuple[str, str, str]] = {
115
+ # Core commands
116
+ "init": (".commands.init", "app", "Initialise the .praisonai/ project convention"),
117
+ "config": (".commands.config", "app", "Configuration management"),
118
+ "traces": (".commands.traces", "app", "Trace collection management"),
119
+ "env": (".commands.environment", "app", "Environment and diagnostics"),
120
+ "auth": (".commands.auth", "app", "Credential management"),
121
+ "session": (".commands.session", "app", "Session management"),
122
+ "completion": (".commands.completion", "app", "Shell completion scripts"),
123
+ "version": (".commands.version", "app", "Version information"),
124
+ "debug": (".commands.debug", "app", "Debug and test interactive flows"),
125
+ "lsp": (".commands.lsp", "app", "LSP service lifecycle"),
126
+ "diag": (".commands.diag", "app", "Diagnostics export"),
127
+ "doctor": (".commands.doctor", "app", "Health checks and diagnostics"),
128
+ "setup": (".commands.setup", "app", "Interactive onboarding / configuration wizard"),
129
+ "onboard": (".commands.onboard", "app", "Messaging bot onboarding wizard"),
130
+ "obs": (".commands.obs", "app", "Observability diagnostics and management"),
131
+ "validate": (".commands.validate", "app", "Validate YAML configuration files"),
132
+ "acp": (".commands.acp", "app", "Agent Client Protocol server"),
133
+ "mcp": (".commands.mcp", "app", "MCP server management"),
134
+ "serve": (".commands.serve", "app", "API server management"),
135
+ "daemon": (".commands.daemon", "app", "Warm local runtime (keeps MCP/provider clients hot)"),
136
+ "attach": (".commands.attach", "app", "Attach to a live session on the warm runtime"),
137
+ "schedule": (".commands.schedule", "app", "Scheduler management"),
138
+ "kanban": (".commands.kanban", "app", "Kanban task management"),
139
+ "run": (".commands.run", "app", "Run agents"),
140
+ "checkpoint": (".commands.checkpoint", "app", "File-level checkpoint management (save/restore/diff)"),
141
+ "profile": (".commands.profile", "app", "Performance profiling and diagnostics"),
142
+ "benchmark": (".commands.benchmark", "app", "Comprehensive performance benchmarking"),
143
+ "paths": (".commands.paths", "app", "Storage path inspection and migration"),
144
+
145
+ # Terminal-native commands
146
+ "chat": (".commands.chat", "app", "Terminal-native interactive chat (REPL)"),
147
+ "code": (".commands.code", "app", "Terminal-native code assistant"),
148
+ "call": (".commands.call", "app", "Voice/call interaction mode"),
149
+ "realtime": (".commands.realtime", "app", "Realtime interaction mode"),
150
+ "train": (".commands.train", "app", "Model training and fine-tuning"),
151
+ "ui": (".commands.ui", "app", "🤖 Clean Chat UI (praisonaiui)"),
152
+ "context": (".commands.context", "app", "Context management"),
153
+ "research": (".commands.research", "app", "Research and analysis"),
154
+ "memory": (".commands.memory", "app", "Memory management"),
155
+ "workflow": (".commands.workflow", "app", "Workflow management"),
156
+ "tools": (".commands.tools", "app", "Tool management"),
157
+ "n8n": (".commands.n8n", "app", "n8n visual workflow editor integration"),
158
+ "knowledge": (".commands.knowledge", "app", "Knowledge base management (legacy)"),
159
+ "rag": (".commands.rag", "app", "RAG commands (legacy - use index/query instead)"),
160
+ "deploy": (".commands.deploy", "app", "Deployment management"),
161
+ "agents": (".commands.agents", "app", "Agent management"),
162
+ "agent": (".commands.agent", "app", "Custom agent definitions management"),
163
+ "command": (".commands.command", "app", "Custom command definitions management"),
164
+ "skills": (".commands.skills", "app", "Skill management"),
165
+ "eval": (".commands.eval", "app", "Evaluation and testing"),
166
+ "templates": (".commands.templates", "app", "Template management"),
167
+ "recipe": (".commands.recipe", "app", "Recipe management"),
168
+ "todo": (".commands.todo", "app", "Todo/task management"),
169
+ "docs": (".commands.docs", "app", "Documentation management"),
170
+ "commit": (".commands.commit", "app", "AI-assisted git commits"),
171
+ "publish": (".commands.publish", "app", "Package publishing"),
172
+ "hooks": (".commands.hooks", "app", "Hook management"),
173
+ "rules": (".commands.rules", "app", "Rules management"),
174
+ "permissions": (".commands.permissions", "permissions", "Tool approval and permission management"),
175
+ "registry": (".commands.registry", "app", "Registry management"),
176
+ "package": (".commands.package", "app", "Package management"),
177
+ "endpoints": (".commands.endpoints", "app", "API endpoint management"),
178
+ "test": (".commands.test", "app", "Run test suite with tier and provider options"),
179
+ "examples": (".commands.examples", "app", "Run and manage example files"),
180
+ "batch": (".commands.batch", "app", "Run all PraisonAI scripts in current folder"),
181
+ "replay": (".commands.replay", "app", "Context replay for debugging agent execution"),
182
+ "loop": (".commands.loop", "app", "Autonomous agent execution loops"),
183
+ "tracker": (".commands.tracker", "app", "Autonomous agent tracking with step-by-step analysis"),
184
+ "github": (".commands.github", "app", "GitHub native context tracking and Issue triage"),
185
+ "managed": (".commands.managed", "app", "Managed Agents (Anthropic cloud-hosted backend)"),
186
+ "models": (".commands.models", "app", "List and describe available models"),
187
+
188
+ # Moltbot-inspired commands
189
+ "bot": (".commands.bot", "app", "Messaging bots with full agent capabilities"),
190
+ "gateway": (".commands.gateway", "app", "Multi-bot WebSocket gateway server"),
191
+ "pairing": (".commands.pairing", "app", "Manage bot user pairing"),
192
+ "identity": (".commands.identity", "app", "Manage cross-platform user identity links"),
193
+ "browser": (".commands.browser", "app", "Browser control for agent automation"),
194
+ "plugins": (".commands.plugins", "app", "Plugin management and inspection"),
195
+ "sandbox": (".commands.sandbox", "app", "Sandbox container management"),
196
+ "claw": (".commands.claw", "app", "🦞 PraisonAI Dashboard (full UI)"),
197
+ "flow": (".commands.flow", "app", "Visual workflow builder (Langflow)"),
198
+ "dashboard": (".commands.dashboard", "app", "🌟 Unified Dashboard (Flow + Claw + UI)"),
199
+ "langfuse": (".commands.langfuse", "app", "🔍 Langfuse observability platform"),
200
+ "langextract": (".commands.langextract", "app", "🧠 Langextract visual trace layer"),
201
+ "port": (".commands.port", "app", "🔌 Manage port usage and resolve conflicts"),
202
+ "up": (".commands.up", "app", "🚀 Start unified PraisonAI stack (Langfuse + Langflow)"),
203
+ }
204
+
205
+ # Special commands that need custom handling
206
+ _SPECIAL_COMMANDS = {
207
+ "tui": (".features.tui.debug", "create_debug_app", "Interactive TUI and simulation"),
208
+ "queue": (".features.tui.cli", "create_queue_app", "Queue management"),
209
+ }
210
+
211
+
212
+ class LazyCommandGroup(TyperGroup):
213
+ """Click Group that lazily loads subcommands from registry."""
214
+
215
+ def list_commands(self, ctx: click.Context) -> List[str]:
216
+ """Return list of available commands without importing them."""
217
+ # Start with commands from parent (already registered commands)
218
+ commands = set(super().list_commands(ctx))
219
+
220
+ # Add lazy-loaded commands
221
+ commands.update(_LAZY_COMMANDS.keys())
222
+ commands.update(_SPECIAL_COMMANDS.keys())
223
+
224
+ # Add special inline commands
225
+ commands.add("app")
226
+
227
+ # Add retrieval commands (these are registered via register_commands)
228
+ commands.update(["index", "query", "search"])
229
+
230
+ # Add standardise/standardize
231
+ commands.update(["standardise", "standardize"])
232
+
233
+ return sorted(list(commands))
234
+
235
+ def get_command(self, ctx: click.Context, name: str) -> Optional[click.Command]:
236
+ """Lazily import and return the command."""
237
+ # First check if command is already registered (e.g., retrieval commands)
238
+ existing = super().get_command(ctx, name)
239
+ if existing is not None:
240
+ return existing
241
+
242
+ # Check regular lazy commands
243
+ if name in _LAZY_COMMANDS:
244
+ module_path, attr_name, _ = _LAZY_COMMANDS[name]
245
+ try:
246
+ module = importlib.import_module(module_path, __package__)
247
+ sub_app = getattr(module, attr_name)
248
+ if isinstance(sub_app, click.Command):
249
+ return sub_app
250
+ return typer_get_command(sub_app)
251
+ except (ImportError, AttributeError) as e:
252
+ typer.echo(f"Error loading command '{name}': {e}", err=True)
253
+ return None
254
+
255
+ # Check special commands
256
+ if name in _SPECIAL_COMMANDS:
257
+ module_path, func_name, _ = _SPECIAL_COMMANDS[name]
258
+ try:
259
+ module = importlib.import_module(module_path, __package__)
260
+ create_func = getattr(module, func_name)
261
+ sub_app = create_func()
262
+ if sub_app:
263
+ if isinstance(sub_app, click.Command):
264
+ return sub_app
265
+ return typer_get_command(sub_app)
266
+ except (ImportError, AttributeError) as e:
267
+ typer.echo(f"Error loading command '{name}': {e}", err=True)
268
+ return None
269
+
270
+ # Handle standardise/standardize
271
+ if name in ["standardise", "standardize"]:
272
+ return self._get_standardise_command()
273
+
274
+ # Handle app command
275
+ if name == "app":
276
+ return self._get_app_command()
277
+
278
+ return None
279
+
280
+ def _get_standardise_command(self) -> Optional[click.Command]:
281
+ """Get the standardise command group."""
282
+ try:
283
+ standardise_app = typer.Typer()
284
+
285
+ @standardise_app.command("check")
286
+ def standardise_check(
287
+ path: str = typer.Option(".", "--path", "-p", help="Project root path"),
288
+ feature: str = typer.Option(None, "--feature", help="Specific feature slug"),
289
+ scope: str = typer.Option("all", "--scope", help="Scope: all, docs, examples, sdk, cli"),
290
+ ci: bool = typer.Option(False, "--ci", help="CI mode"),
291
+ ):
292
+ """Check for standardisation issues."""
293
+ from .commands.standardise import _run_check
294
+ import argparse
295
+ args = argparse.Namespace(path=path, feature=feature, scope=scope, ci=ci, dry_run=True)
296
+ _run_check(args)
297
+
298
+ @standardise_app.command("report")
299
+ def standardise_report(
300
+ path: str = typer.Option(".", "--path", "-p", help="Project root path"),
301
+ format: str = typer.Option("text", "--format", "-f", help="Format: text, json, markdown"),
302
+ output: str = typer.Option(None, "--output", "-o", help="Output file"),
303
+ ci: bool = typer.Option(False, "--ci", help="CI mode"),
304
+ ):
305
+ """Generate detailed report."""
306
+ from .commands.standardise import _run_report
307
+ import argparse
308
+ args = argparse.Namespace(path=path, format=format, output=output, ci=ci, feature=None, scope="all", dry_run=True)
309
+ _run_report(args)
310
+
311
+ @standardise_app.command("fix")
312
+ def standardise_fix(
313
+ path: str = typer.Option(".", "--path", "-p", help="Project root path"),
314
+ feature: str = typer.Option(None, "--feature", help="Specific feature slug"),
315
+ apply: bool = typer.Option(False, "--apply", help="Actually apply changes"),
316
+ no_backup: bool = typer.Option(False, "--no-backup", help="Don't create backups"),
317
+ ):
318
+ """Fix standardisation issues."""
319
+ from .commands.standardise import _run_fix
320
+ import argparse
321
+ args = argparse.Namespace(path=path, feature=feature, apply=apply, no_backup=no_backup, scope="all", ci=False, dry_run=not apply)
322
+ _run_fix(args)
323
+
324
+ @standardise_app.command("init")
325
+ def standardise_init(
326
+ feature: str = typer.Argument(..., help="Feature slug to initialise"),
327
+ path: str = typer.Option(".", "--path", "-p", help="Project root path"),
328
+ apply: bool = typer.Option(False, "--apply", help="Actually create files"),
329
+ ):
330
+ """Initialise a new feature with all required artifacts."""
331
+ from .commands.standardise import _run_init
332
+ import argparse
333
+ args = argparse.Namespace(feature=feature, path=path, apply=apply, scope="all", ci=False, dry_run=not apply)
334
+ _run_init(args)
335
+
336
+ @standardise_app.command("ai")
337
+ def standardise_ai(
338
+ feature: str = typer.Argument(..., help="Feature slug to generate content for"),
339
+ gen_type: str = typer.Option("all", "--type", "-t", help="Type: docs, examples, all"),
340
+ apply: bool = typer.Option(False, "--apply", help="Actually create files"),
341
+ verify: bool = typer.Option(False, "--verify", help="Verify with AI"),
342
+ model: str = typer.Option("gpt-4o-mini", "--model", help="LLM model"),
343
+ path: str = typer.Option(".", "--path", "-p", help="Project root path"),
344
+ ):
345
+ """AI-powered generation of docs/examples."""
346
+ from .commands.standardise import _run_ai
347
+ import argparse
348
+ args = argparse.Namespace(feature=feature, type=gen_type, apply=apply, verify=verify, model=model, path=path, scope="all", ci=False, dry_run=not apply)
349
+ _run_ai(args)
350
+
351
+ @standardise_app.command("checkpoint")
352
+ def standardise_checkpoint(
353
+ message: str = typer.Option(None, "--message", "-m", help="Checkpoint message"),
354
+ path: str = typer.Option(".", "--path", "-p", help="Repository path"),
355
+ ):
356
+ """Create an undo checkpoint."""
357
+ from .commands.standardise import _run_checkpoint
358
+ import argparse
359
+ args = argparse.Namespace(message=message, path=path)
360
+ _run_checkpoint(args)
361
+
362
+ @standardise_app.command("undo")
363
+ def standardise_undo(
364
+ checkpoint: str = typer.Option(None, "--checkpoint", help="Checkpoint ID"),
365
+ list_checkpoints: bool = typer.Option(False, "--list", help="List checkpoints"),
366
+ path: str = typer.Option(".", "--path", "-p", help="Repository path"),
367
+ ):
368
+ """Undo to a previous checkpoint."""
369
+ from .commands.standardise import _run_undo
370
+ import argparse
371
+ args = argparse.Namespace(checkpoint=checkpoint, list=list_checkpoints, path=path)
372
+ _run_undo(args)
373
+
374
+ @standardise_app.command("redo")
375
+ def standardise_redo(
376
+ path: str = typer.Option(".", "--path", "-p", help="Repository path"),
377
+ ):
378
+ """Redo after an undo."""
379
+ from .commands.standardise import _run_redo
380
+ import argparse
381
+ args = argparse.Namespace(path=path)
382
+ _run_redo(args)
383
+
384
+ return typer_get_command(standardise_app)
385
+ except Exception:
386
+ return None
387
+
388
+ def _get_app_command(self) -> Optional[click.Command]:
389
+ """Get the app command."""
390
+ # Create a local Typer app to avoid mutating the global app
391
+ app_group = typer.Typer(add_completion=False)
392
+
393
+ @app_group.command(name="app", context_settings={"allow_interspersed_args": False})
394
+ def app_cmd(
395
+ port: int = typer.Option(8000, "--port", "-p", help="Port to listen on"),
396
+ host: str = typer.Option("0.0.0.0", "--host", "-h", help="Host to bind to"),
397
+ config: str = typer.Option(None, "--config", "-c", help="Path to config file (YAML)"),
398
+ reload: bool = typer.Option(False, "--reload", "-r", help="Enable auto-reload for development"),
399
+ debug: bool = typer.Option(False, "--debug", "-d", help="Enable debug mode"),
400
+ name: str = typer.Option("PraisonAI App", "--name", "-n", help="Application name"),
401
+ ):
402
+ """
403
+ Start an AgentOS server for production deployment.
404
+
405
+ AgentOS provides a FastAPI-based web service for deploying AI agents
406
+ with REST and WebSocket endpoints.
407
+ """
408
+ from rich.console import Console
409
+ console = Console()
410
+
411
+ try:
412
+ from praisonai import AgentOS
413
+ from praisonaiagents import AgentOSConfig
414
+ except ImportError as e:
415
+ console.print(f"[red]Error importing AgentOS: {e}[/red]")
416
+ console.print("[yellow]Install with: pip install praisonai[api][/yellow]")
417
+ raise typer.Abort()
418
+
419
+ # Load agents from config file if provided
420
+ agents = []
421
+ if config:
422
+ agents = self._load_agents_from_config_file(config, console)
423
+
424
+ # Create config
425
+ app_config = AgentOSConfig(
426
+ name=name,
427
+ host=host,
428
+ port=port,
429
+ reload=reload,
430
+ debug=debug,
431
+ )
432
+
433
+ # Create and start app
434
+ console.print(f"\n[bold green]🚀 Starting {name}[/bold green]")
435
+ console.print(f"[dim]Host: {host}:{port}[/dim]")
436
+ if agents:
437
+ console.print(f"[dim]Agents: {len(agents)}[/dim]")
438
+ if reload:
439
+ console.print("[yellow]Auto-reload enabled (development mode)[/yellow]")
440
+ console.print()
441
+
442
+ try:
443
+ agent_app = AgentOS(
444
+ name=name,
445
+ agents=agents,
446
+ config=app_config,
447
+ )
448
+ agent_app.serve()
449
+ except ImportError as e:
450
+ console.print(f"[red]Missing dependency: {e}[/red]")
451
+ console.print("[yellow]Install with: pip install praisonai[api][/yellow]")
452
+ raise typer.Abort()
453
+ except Exception as e:
454
+ console.print(f"[red]Error starting server: {e}[/red]")
455
+ raise typer.Abort()
456
+
457
+ # Bind the helper method to the function for later use
458
+ app_cmd._load_agents_from_config_file = self._load_agents_from_config_file
459
+
460
+ # Return the click.Command object (not the raw function)
461
+ return typer_get_command(app_group)
462
+
463
+ def _load_agents_from_config_file(self, config_path: str, console) -> list:
464
+ """Load agents from a YAML config file."""
465
+ import yaml
466
+
467
+ try:
468
+ with open(config_path, 'r') as f:
469
+ config_data = yaml.safe_load(f)
470
+ except Exception as e:
471
+ console.print(f"[red]Error loading config: {e}[/red]")
472
+ return []
473
+
474
+ if not config_data:
475
+ return []
476
+
477
+ agents = []
478
+
479
+ # Try to load agents from config
480
+ agents_config = config_data.get('agents', [])
481
+ if not agents_config and 'agent' in config_data:
482
+ agents_config = [config_data['agent']]
483
+
484
+ if agents_config:
485
+ try:
486
+ from praisonaiagents import Agent
487
+
488
+ for agent_data in agents_config:
489
+ if isinstance(agent_data, dict):
490
+ agent = Agent(
491
+ name=agent_data.get('name', 'Agent'),
492
+ role=agent_data.get('role'),
493
+ instructions=agent_data.get('instructions', agent_data.get('goal', '')),
494
+ llm=agent_data.get('llm'),
495
+ )
496
+ agents.append(agent)
497
+ console.print(f"[green]✓ Loaded agent: {agent.name}[/green]")
498
+ except Exception as e:
499
+ console.print(f"[yellow]Warning: Could not load agents from config: {e}[/yellow]")
500
+
501
+ return agents
502
+
503
+
504
+ # Create main Typer app with lazy loading group
505
+ app = typer.Typer(
506
+ name="praisonai",
507
+ help="PraisonAI - AI Agents Framework CLI",
508
+ add_completion=False, # We handle completion manually
509
+ no_args_is_help=False, # Allow running without args for legacy compatibility
510
+ rich_markup_mode="rich",
511
+ cls=LazyCommandGroup, # Use our lazy loading command group
512
+ )
513
+
514
+
515
+ # Global state for options
516
+ class GlobalState:
517
+ """Global state for CLI options."""
518
+ output_format: OutputFormat = OutputFormat.text
519
+ no_color: bool = False
520
+ quiet: bool = False
521
+ verbose: bool = False
522
+ screen_reader: bool = False
523
+ observe: Optional[str] = None
524
+ output_controller: Optional[OutputController] = None
525
+
526
+
527
+ state = GlobalState()
528
+
529
+
530
+ def version_callback(value: bool):
531
+ """Show version and exit."""
532
+ if value:
533
+ from praisonai.version import __version__
534
+ typer.echo(f"PraisonAI version {__version__}")
535
+ raise typer.Exit()
536
+
537
+
538
+ @app.callback(invoke_without_command=True)
539
+ def main_callback(
540
+ ctx: typer.Context,
541
+ version: bool = typer.Option(
542
+ False,
543
+ "--version",
544
+ "-V",
545
+ callback=version_callback,
546
+ is_eager=True,
547
+ help="Show version and exit",
548
+ ),
549
+ output_format: OutputFormat = typer.Option(
550
+ OutputFormat.text,
551
+ "--output-format",
552
+ "-o",
553
+ help="Output format",
554
+ envvar="PRAISONAI_OUTPUT_FORMAT",
555
+ ),
556
+ json_output: bool = typer.Option(
557
+ False,
558
+ "--json",
559
+ help="Output in JSON format (alias for --output-format json)",
560
+ ),
561
+ no_color: bool = typer.Option(
562
+ False,
563
+ "--no-color",
564
+ help="Disable colored output",
565
+ envvar="NO_COLOR",
566
+ ),
567
+ quiet: bool = typer.Option(
568
+ False,
569
+ "--quiet",
570
+ "-q",
571
+ help="Minimal output",
572
+ ),
573
+ verbose: bool = typer.Option(
574
+ False,
575
+ "--verbose",
576
+ "-v",
577
+ help="Verbose output with debug details",
578
+ ),
579
+ screen_reader: bool = typer.Option(
580
+ False,
581
+ "--screen-reader",
582
+ help="Screen reader friendly output (no spinners/panels)",
583
+ ),
584
+ observe: Optional[str] = typer.Option(
585
+ None,
586
+ "--observe",
587
+ "-O",
588
+ help="Enable observability (langfuse, langextract)",
589
+ envvar="PRAISONAI_OBSERVE",
590
+ ),
591
+ ):
592
+ """
593
+ PraisonAI - AI Agents Framework CLI.
594
+
595
+ Run agents, manage configuration, and more.
596
+ """
597
+ # Store global options
598
+ state.output_format = output_format
599
+ state.no_color = no_color
600
+ state.quiet = quiet
601
+ state.verbose = verbose
602
+ state.screen_reader = screen_reader
603
+ state.observe = observe
604
+
605
+ # Handle --json alias
606
+ if json_output:
607
+ state.output_format = OutputFormat.json
608
+
609
+ # Validate and set up observability if requested
610
+ if observe:
611
+ if observe == "langfuse":
612
+ _setup_langfuse_observability(verbose=verbose)
613
+ elif observe == "langextract":
614
+ _setup_langextract_observability(verbose=verbose)
615
+ else:
616
+ raise typer.BadParameter(
617
+ f"Unsupported observe provider: {observe}. "
618
+ "Choose one of: langfuse, langextract."
619
+ )
620
+
621
+ # Determine output mode
622
+ if state.quiet:
623
+ mode = OutputMode.QUIET
624
+ elif state.verbose:
625
+ mode = OutputMode.VERBOSE
626
+ elif state.screen_reader:
627
+ mode = OutputMode.SCREEN_READER
628
+ elif state.output_format == OutputFormat.json:
629
+ mode = OutputMode.JSON
630
+ elif state.output_format == OutputFormat.stream_json:
631
+ mode = OutputMode.STREAM_JSON
632
+ else:
633
+ mode = OutputMode.TEXT
634
+
635
+ # Install warning filters for CLI usage only
636
+ from ._warnings import install_warning_filters
637
+ install_warning_filters()
638
+
639
+ # Create run context
640
+ context = create_context()
641
+
642
+ # Create and set output controller
643
+ state.output_controller = OutputController(
644
+ mode=mode,
645
+ no_color=state.no_color,
646
+ run_id=context.run_id,
647
+ trace_id=context.trace_id,
648
+ )
649
+ set_output_controller(state.output_controller)
650
+
651
+ # If no command provided, start interactive mode
652
+ if ctx.invoked_subcommand is None:
653
+ # Check for credentials before starting TUI
654
+ from praisonai.llm.credentials import is_configured
655
+ import sys
656
+
657
+ if not is_configured(): # Check for any configured credentials
658
+ # In non-interactive mode, just show error
659
+ if not sys.stdin.isatty() or quiet:
660
+ typer.echo(
661
+ "Error: No API key configured. Run: praisonai setup",
662
+ err=True
663
+ )
664
+ raise typer.Exit(1)
665
+
666
+ # In interactive mode, offer to run setup
667
+ typer.echo("No API key configured.")
668
+ run_setup = typer.confirm("Would you like to run the setup wizard now?")
669
+
670
+ if run_setup:
671
+ # Import and run setup
672
+ from .commands.setup import _run_setup
673
+ exit_code = _run_setup(
674
+ non_interactive=False,
675
+ provider=None,
676
+ api_key=None,
677
+ model=None
678
+ )
679
+ if exit_code != 0:
680
+ typer.echo("Setup failed. Exiting.", err=True)
681
+ raise typer.Exit(exit_code)
682
+
683
+ # Re-check credentials after setup
684
+ if not is_configured():
685
+ typer.echo("Setup completed but credentials still not detected.", err=True)
686
+ raise typer.Exit(1)
687
+
688
+ # After successful setup, continue to TUI
689
+ typer.echo("\nSetup complete! Starting interactive mode...\n")
690
+ else:
691
+ typer.echo("\nTo configure credentials later, run: praisonai setup")
692
+ typer.echo("or set environment variables like OPENAI_API_KEY")
693
+ raise typer.Exit(0)
694
+
695
+ from .interactive.async_tui import AsyncTUI, AsyncTUIConfig
696
+
697
+ tui_config = AsyncTUIConfig(
698
+ model="gpt-4o-mini",
699
+ show_logo=True,
700
+ show_status_bar=state.output_format != OutputFormat.json,
701
+ )
702
+
703
+ tui = AsyncTUI(config=tui_config)
704
+ tui.run()
705
+
706
+
707
+ def get_output_controller() -> OutputController:
708
+ """Get the current output controller."""
709
+ if state.output_controller is None:
710
+ state.output_controller = OutputController()
711
+ return state.output_controller
712
+
713
+
714
+ # Import and register command groups
715
+ _commands_registered = False
716
+
717
+ def get_command_names():
718
+ """Get all available command names without importing the modules.
719
+
720
+ Derived from the authoritative ``_LAZY_COMMANDS`` registry (the single
721
+ source of truth that also drives ``--help``) so routing can never drift
722
+ from the advertised command set. Special and dynamically-registered
723
+ commands that are not in ``_LAZY_COMMANDS`` are added explicitly.
724
+ """
725
+ names = set(_LAZY_COMMANDS.keys())
726
+ # Special commands with custom handling (tui, queue)
727
+ names.update(_SPECIAL_COMMANDS.keys())
728
+ # Inline special commands handled outside the registries
729
+ names.update({"app", "standardise", "standardize"})
730
+ # Dynamically registered retrieval commands (no static module entry)
731
+ # NOTE: retrieval_module.register_commands(app) adds these commands dynamically
732
+ names.update({"index", "query"})
733
+ return names
734
+
735
+ def register_commands():
736
+ """Register all command groups (idempotent).
737
+
738
+ With lazy loading, this function now only registers the retrieval commands
739
+ that need special handling. All other commands are loaded on-demand through
740
+ the LazyCommandGroup.
741
+ """
742
+ global _commands_registered
743
+ if _commands_registered:
744
+ return
745
+
746
+ # Register retrieval commands (these need special handling)
747
+ try:
748
+ from .commands import retrieval as retrieval_module
749
+ retrieval_module.register_commands(app)
750
+ except ImportError:
751
+ pass # Graceful degradation
752
+
753
+ # Mark registration complete
754
+ _commands_registered = True
755
+
756
+
757
+ # Commands will be registered lazily when needed