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,609 @@
1
+ """
2
+ Plugins command group for PraisonAI CLI.
3
+
4
+ Provides plugin management commands inspired by moltbot's plugins CLI.
5
+ Supports listing, enabling, disabling, and inspecting plugins.
6
+ """
7
+
8
+ import typer
9
+
10
+ app = typer.Typer(
11
+ help="Plugin management and inspection",
12
+ no_args_is_help=True,
13
+ )
14
+
15
+
16
+ @app.command("list")
17
+ def plugins_list(
18
+ enabled_only: bool = typer.Option(False, "--enabled", help="Show only enabled plugins"),
19
+ json_output: bool = typer.Option(False, "--json", help="Output JSON"),
20
+ ):
21
+ """List available plugins.
22
+
23
+ Examples:
24
+ praisonai plugins list
25
+ praisonai plugins list --enabled
26
+ praisonai plugins list --json
27
+ """
28
+ try:
29
+ # Try to get plugins from the plugin system
30
+ plugins = _get_available_plugins()
31
+
32
+ if enabled_only:
33
+ plugins = [p for p in plugins if p.get("enabled", False)]
34
+
35
+ if json_output:
36
+ import json
37
+ print(json.dumps(plugins, indent=2))
38
+ else:
39
+ from rich.console import Console
40
+ from rich.table import Table
41
+
42
+ console = Console()
43
+ table = Table(title=f"Plugins ({len(plugins)} available)")
44
+ table.add_column("ID", style="cyan")
45
+ table.add_column("Name")
46
+ table.add_column("Status")
47
+ table.add_column("Description")
48
+
49
+ for plugin in plugins:
50
+ status = "[green]enabled[/green]" if plugin.get("enabled") else "[dim]disabled[/dim]"
51
+ table.add_row(
52
+ plugin.get("id", "-"),
53
+ plugin.get("name", "-"),
54
+ status,
55
+ plugin.get("description", "-")[:40] + "..." if len(plugin.get("description", "")) > 40 else plugin.get("description", "-"),
56
+ )
57
+
58
+ console.print(table)
59
+
60
+ except Exception as e:
61
+ typer.echo(f"Error listing plugins: {e}", err=True)
62
+ raise typer.Exit(1)
63
+
64
+
65
+ @app.command("info")
66
+ def plugins_info(
67
+ plugin_id: str = typer.Argument(..., help="Plugin ID"),
68
+ ):
69
+ """Show detailed information about a plugin.
70
+
71
+ Examples:
72
+ praisonai plugins info memory-core
73
+ praisonai plugins info browser-tool
74
+ """
75
+ try:
76
+ plugins = _get_available_plugins()
77
+ plugin = next((p for p in plugins if p.get("id") == plugin_id), None)
78
+
79
+ if not plugin:
80
+ typer.echo(f"Plugin not found: {plugin_id}", err=True)
81
+ raise typer.Exit(1)
82
+
83
+ from rich.console import Console
84
+ from rich.panel import Panel
85
+
86
+ console = Console()
87
+
88
+ console.print(f"\n[bold cyan]{plugin.get('name', plugin_id)}[/bold cyan]")
89
+ console.print(f"ID: {plugin.get('id', '-')}")
90
+ console.print(f"Status: {'[green]enabled[/green]' if plugin.get('enabled') else '[dim]disabled[/dim]'}")
91
+ console.print(f"Description: {plugin.get('description', '-')}")
92
+
93
+ if plugin.get("version"):
94
+ console.print(f"Version: {plugin.get('version')}")
95
+ if plugin.get("author"):
96
+ console.print(f"Author: {plugin.get('author')}")
97
+ if plugin.get("hooks"):
98
+ console.print(f"Hooks: {', '.join(plugin.get('hooks', []))}")
99
+ if plugin.get("config_schema"):
100
+ console.print("\n[bold]Config Schema:[/bold]")
101
+ import json
102
+ console.print(Panel(json.dumps(plugin.get("config_schema"), indent=2)))
103
+
104
+ except Exception as e:
105
+ typer.echo(f"Error: {e}", err=True)
106
+ raise typer.Exit(1)
107
+
108
+
109
+ @app.command("enable")
110
+ def plugins_enable(
111
+ plugin_id: str = typer.Argument(..., help="Plugin ID to enable"),
112
+ ):
113
+ """Enable a plugin.
114
+
115
+ Examples:
116
+ praisonai plugins enable memory-core
117
+ praisonai plugins enable browser-tool
118
+ """
119
+ try:
120
+ # Update config to enable plugin
121
+ config_path = _get_config_path()
122
+ config = _load_config(config_path)
123
+
124
+ if "plugins" not in config:
125
+ config["plugins"] = {}
126
+ if "enabled" not in config["plugins"]:
127
+ config["plugins"]["enabled"] = []
128
+
129
+ if plugin_id not in config["plugins"]["enabled"]:
130
+ config["plugins"]["enabled"].append(plugin_id)
131
+ _save_config(config_path, config)
132
+ typer.echo(f"[green]✓[/green] Plugin enabled: {plugin_id}")
133
+ else:
134
+ typer.echo(f"Plugin already enabled: {plugin_id}")
135
+
136
+ except Exception as e:
137
+ typer.echo(f"Error enabling plugin: {e}", err=True)
138
+ raise typer.Exit(1)
139
+
140
+
141
+ @app.command("disable")
142
+ def plugins_disable(
143
+ plugin_id: str = typer.Argument(..., help="Plugin ID to disable"),
144
+ ):
145
+ """Disable a plugin.
146
+
147
+ Examples:
148
+ praisonai plugins disable memory-core
149
+ praisonai plugins disable browser-tool
150
+ """
151
+ try:
152
+ config_path = _get_config_path()
153
+ config = _load_config(config_path)
154
+
155
+ if "plugins" in config and "enabled" in config["plugins"]:
156
+ if plugin_id in config["plugins"]["enabled"]:
157
+ config["plugins"]["enabled"].remove(plugin_id)
158
+ _save_config(config_path, config)
159
+ typer.echo(f"[yellow]![/yellow] Plugin disabled: {plugin_id}")
160
+ else:
161
+ typer.echo(f"Plugin not enabled: {plugin_id}")
162
+ else:
163
+ typer.echo(f"Plugin not enabled: {plugin_id}")
164
+
165
+ except Exception as e:
166
+ typer.echo(f"Error disabling plugin: {e}", err=True)
167
+ raise typer.Exit(1)
168
+
169
+
170
+ @app.command("doctor")
171
+ def plugins_doctor():
172
+ """Check plugin health and configuration.
173
+
174
+ Verifies that all enabled plugins are properly configured and
175
+ their dependencies are met.
176
+
177
+ Examples:
178
+ praisonai plugins doctor
179
+ """
180
+ try:
181
+ from rich.console import Console
182
+ from rich.table import Table
183
+
184
+ console = Console()
185
+ plugins = _get_available_plugins()
186
+ enabled_plugins = [p for p in plugins if p.get("enabled")]
187
+
188
+ console.print("[bold]Plugin Health Check[/bold]\n")
189
+
190
+ if not enabled_plugins:
191
+ console.print("[yellow]No plugins enabled[/yellow]")
192
+ return
193
+
194
+ table = Table()
195
+ table.add_column("Plugin", style="cyan")
196
+ table.add_column("Status")
197
+ table.add_column("Issues")
198
+
199
+ issues_found = 0
200
+
201
+ for plugin in enabled_plugins:
202
+ issues = []
203
+
204
+ # Check if plugin module exists
205
+ if plugin.get("module"):
206
+ try:
207
+ __import__(plugin["module"])
208
+ except ImportError:
209
+ issues.append("Module not found")
210
+
211
+ # Check required config
212
+ if plugin.get("required_config"):
213
+ for key in plugin["required_config"]:
214
+ # Check if config key exists
215
+ pass # Would check actual config
216
+
217
+ if issues:
218
+ status = "[red]✗ Issues[/red]"
219
+ issues_found += len(issues)
220
+ else:
221
+ status = "[green]✓ OK[/green]"
222
+
223
+ table.add_row(
224
+ plugin.get("name", plugin.get("id")),
225
+ status,
226
+ ", ".join(issues) if issues else "-",
227
+ )
228
+
229
+ console.print(table)
230
+
231
+ if issues_found:
232
+ console.print(f"\n[red]Found {issues_found} issue(s)[/red]")
233
+ else:
234
+ console.print(f"\n[green]All {len(enabled_plugins)} plugins healthy[/green]")
235
+
236
+ except Exception as e:
237
+ typer.echo(f"Error: {e}", err=True)
238
+ raise typer.Exit(1)
239
+
240
+
241
+ # ============ Single-File Plugin Commands ============
242
+
243
+ @app.command("create")
244
+ def plugins_create(
245
+ name: str = typer.Argument(..., help="Plugin name (will be converted to snake_case)"),
246
+ description: str = typer.Option("A PraisonAI plugin", "--description", "-d", help="Plugin description"),
247
+ author: str = typer.Option("", "--author", "-a", help="Plugin author"),
248
+ output_dir: str = typer.Option(None, "--output", "-o", help="Output directory (default: ~/.praison/plugins/)"),
249
+ ):
250
+ """Create a new single-file plugin from template.
251
+
252
+ Creates a new plugin with WordPress-style docstring header in the
253
+ user's plugin directory (~/.praison/plugins/).
254
+
255
+ Examples:
256
+ praisonai plugins create weather
257
+ praisonai plugins create weather --description "Weather API tools"
258
+ praisonai plugins create weather -o ./my_plugins/
259
+ """
260
+ try:
261
+ from pathlib import Path
262
+ from praisonaiagents.plugins.discovery import get_plugin_template, ensure_plugin_dir
263
+
264
+ # Sanitize plugin name
265
+ safe_name = name.lower().replace("-", "_").replace(" ", "_")
266
+
267
+ # Determine output directory
268
+ if output_dir:
269
+ plugin_dir = Path(output_dir).expanduser().resolve()
270
+ plugin_dir.mkdir(parents=True, exist_ok=True)
271
+ else:
272
+ plugin_dir = ensure_plugin_dir()
273
+
274
+ plugin_path = plugin_dir / f"{safe_name}.py"
275
+
276
+ # Check if file already exists
277
+ if plugin_path.exists():
278
+ overwrite = typer.confirm(f"Plugin {plugin_path} already exists. Overwrite?")
279
+ if not overwrite:
280
+ typer.echo("Cancelled.")
281
+ raise typer.Exit(0)
282
+
283
+ # Generate template
284
+ template = get_plugin_template(name, description, author)
285
+
286
+ # Write file
287
+ plugin_path.write_text(template)
288
+
289
+ typer.echo(f"[green]✓[/green] Created plugin: {plugin_path}")
290
+ typer.echo(f"\nNext steps:")
291
+ typer.echo(f" 1. Edit {plugin_path} to add your tools")
292
+ typer.echo(f" 2. Run 'praisonai plugins discover' to verify")
293
+ typer.echo(f" 3. Use tools with Agent(tools=['example_tool'])")
294
+
295
+ except ImportError as e:
296
+ typer.echo(f"Error: praisonaiagents package not found. Install with: pip install praisonaiagents", err=True)
297
+ raise typer.Exit(1)
298
+ except Exception as e:
299
+ typer.echo(f"Error creating plugin: {e}", err=True)
300
+ raise typer.Exit(1)
301
+
302
+
303
+ @app.command("install")
304
+ def plugins_install(
305
+ source: str = typer.Argument(..., help="Path to plugin file to install"),
306
+ force: bool = typer.Option(False, "--force", "-f", help="Overwrite existing plugin"),
307
+ ):
308
+ """Install a single-file plugin.
309
+
310
+ Copies a plugin file to the user's plugin directory (~/.praison/plugins/).
311
+
312
+ Examples:
313
+ praisonai plugins install ./my_weather_plugin.py
314
+ praisonai plugins install ./my_weather_plugin.py --force
315
+ """
316
+ try:
317
+ from pathlib import Path
318
+ import shutil
319
+ from praisonaiagents.plugins.discovery import ensure_plugin_dir
320
+ from praisonaiagents.plugins.parser import parse_plugin_header_from_file
321
+
322
+ source_path = Path(source).expanduser().resolve()
323
+
324
+ # Validate source file
325
+ if not source_path.exists():
326
+ typer.echo(f"Error: File not found: {source}", err=True)
327
+ raise typer.Exit(1)
328
+
329
+ if not source_path.suffix == ".py":
330
+ typer.echo(f"Error: Plugin must be a Python file (.py)", err=True)
331
+ raise typer.Exit(1)
332
+
333
+ # Validate plugin header
334
+ try:
335
+ metadata = parse_plugin_header_from_file(str(source_path))
336
+ plugin_name = metadata.get("name", source_path.stem)
337
+ except Exception as e:
338
+ typer.echo(f"Error: Invalid plugin header: {e}", err=True)
339
+ raise typer.Exit(1)
340
+
341
+ # Install to user plugin directory
342
+ plugin_dir = ensure_plugin_dir()
343
+ dest_path = plugin_dir / source_path.name
344
+
345
+ if dest_path.exists() and not force:
346
+ overwrite = typer.confirm(f"Plugin {dest_path.name} already exists. Overwrite?")
347
+ if not overwrite:
348
+ typer.echo("Cancelled.")
349
+ raise typer.Exit(0)
350
+
351
+ shutil.copy2(source_path, dest_path)
352
+
353
+ typer.echo(f"[green]✓[/green] Installed '{plugin_name}' to {dest_path}")
354
+ typer.echo(f"\nTools will be available on next run.")
355
+
356
+ except ImportError:
357
+ typer.echo(f"Error: praisonaiagents package not found.", err=True)
358
+ raise typer.Exit(1)
359
+ except typer.Exit:
360
+ raise
361
+ except Exception as e:
362
+ typer.echo(f"Error installing plugin: {e}", err=True)
363
+ raise typer.Exit(1)
364
+
365
+
366
+ @app.command("discover")
367
+ def plugins_discover(
368
+ plugin_dirs: str = typer.Option(None, "--dirs", "-d", help="Additional plugin directories (comma-separated)"),
369
+ load: bool = typer.Option(False, "--load", "-l", help="Actually load the plugins"),
370
+ json_output: bool = typer.Option(False, "--json", help="Output JSON"),
371
+ ):
372
+ """Discover single-file plugins from plugin directories.
373
+
374
+ Scans ~/.praison/plugins/ and ./.praison/plugins/ for valid plugins.
375
+
376
+ Examples:
377
+ praisonai plugins discover
378
+ praisonai plugins discover --load
379
+ praisonai plugins discover --json
380
+ """
381
+ try:
382
+ from praisonaiagents.plugins.discovery import discover_plugins, discover_and_load_plugins
383
+
384
+ extra_dirs = plugin_dirs.split(",") if plugin_dirs else None
385
+
386
+ if load:
387
+ plugins = discover_and_load_plugins(extra_dirs)
388
+ else:
389
+ plugins = discover_plugins(extra_dirs)
390
+
391
+ if json_output:
392
+ import json
393
+ print(json.dumps(plugins, indent=2, default=str))
394
+ else:
395
+ from rich.console import Console
396
+ from rich.table import Table
397
+
398
+ console = Console()
399
+
400
+ if not plugins:
401
+ console.print("[yellow]No single-file plugins found.[/yellow]")
402
+ console.print("\nCreate one with: praisonai plugins init <name>")
403
+ return
404
+
405
+ table = Table(title=f"Single-File Plugins ({len(plugins)} found)")
406
+ table.add_column("Name", style="cyan")
407
+ table.add_column("Version")
408
+ table.add_column("Tools", style="green")
409
+ table.add_column("Path")
410
+
411
+ for plugin in plugins:
412
+ tools = plugin.get("tools", [])
413
+ tools_str = ", ".join(tools[:3])
414
+ if len(tools) > 3:
415
+ tools_str += f" (+{len(tools)-3})"
416
+
417
+ table.add_row(
418
+ plugin.get("name", "-"),
419
+ plugin.get("version", "-"),
420
+ tools_str or "-",
421
+ str(plugin.get("path", "-"))[:40],
422
+ )
423
+
424
+ console.print(table)
425
+
426
+ if load:
427
+ console.print("\n[green]Plugins loaded.[/green] Tools are now available.")
428
+
429
+ except ImportError:
430
+ typer.echo(f"Error: praisonaiagents package not found.", err=True)
431
+ raise typer.Exit(1)
432
+ except Exception as e:
433
+ typer.echo(f"Error discovering plugins: {e}", err=True)
434
+ raise typer.Exit(1)
435
+
436
+
437
+ @app.command("remove")
438
+ def plugins_remove(
439
+ name: str = typer.Argument(..., help="Plugin name or filename to remove"),
440
+ force: bool = typer.Option(False, "--force", "-f", help="Remove without confirmation"),
441
+ ):
442
+ """Remove a single-file plugin.
443
+
444
+ Removes a plugin from the user's plugin directory.
445
+
446
+ Examples:
447
+ praisonai plugins remove weather
448
+ praisonai plugins remove weather_plugin.py --force
449
+ """
450
+ try:
451
+ from pathlib import Path
452
+ from praisonaiagents.plugins.discovery import get_default_plugin_dirs
453
+
454
+ # Normalize name
455
+ if not name.endswith(".py"):
456
+ name = f"{name}.py"
457
+
458
+ # Search in plugin directories
459
+ plugin_path = None
460
+ for plugin_dir in get_default_plugin_dirs():
461
+ candidate = plugin_dir / name
462
+ if candidate.exists():
463
+ plugin_path = candidate
464
+ break
465
+
466
+ # Also check user directory explicitly
467
+ if not plugin_path:
468
+ user_dir = Path.home() / ".praison" / "plugins"
469
+ candidate = user_dir / name
470
+ if candidate.exists():
471
+ plugin_path = candidate
472
+
473
+ if not plugin_path:
474
+ typer.echo(f"Plugin not found: {name}", err=True)
475
+ raise typer.Exit(1)
476
+
477
+ if not force:
478
+ confirm = typer.confirm(f"Remove plugin {plugin_path}?")
479
+ if not confirm:
480
+ typer.echo("Cancelled.")
481
+ raise typer.Exit(0)
482
+
483
+ plugin_path.unlink()
484
+ typer.echo(f"[green]✓[/green] Removed: {plugin_path}")
485
+
486
+ except ImportError:
487
+ # Fallback without praisonaiagents
488
+ from pathlib import Path
489
+ user_dir = Path.home() / ".praison" / "plugins"
490
+ if not name.endswith(".py"):
491
+ name = f"{name}.py"
492
+ plugin_path = user_dir / name
493
+ if plugin_path.exists():
494
+ if force or typer.confirm(f"Remove plugin {plugin_path}?"):
495
+ plugin_path.unlink()
496
+ typer.echo(f"[green]✓[/green] Removed: {plugin_path}")
497
+ else:
498
+ typer.echo(f"Plugin not found: {name}", err=True)
499
+ raise typer.Exit(1)
500
+ except typer.Exit:
501
+ raise
502
+ except Exception as e:
503
+ typer.echo(f"Error removing plugin: {e}", err=True)
504
+ raise typer.Exit(1)
505
+
506
+
507
+ def _get_available_plugins():
508
+ """Get list of available plugins."""
509
+ # Built-in plugins
510
+ plugins = [
511
+ {
512
+ "id": "memory-core",
513
+ "name": "Memory Core",
514
+ "description": "Semantic memory indexing and search",
515
+ "enabled": True,
516
+ "hooks": ["on_message", "on_session_start"],
517
+ },
518
+ {
519
+ "id": "browser-tool",
520
+ "name": "Browser Tool",
521
+ "description": "Browser automation and control",
522
+ "enabled": False,
523
+ "hooks": ["on_tool_call"],
524
+ },
525
+ {
526
+ "id": "knowledge-rag",
527
+ "name": "Knowledge RAG",
528
+ "description": "Retrieval-augmented generation",
529
+ "enabled": False,
530
+ "hooks": ["on_message", "on_context_build"],
531
+ },
532
+ {
533
+ "id": "telemetry",
534
+ "name": "Telemetry",
535
+ "description": "Usage tracking and analytics",
536
+ "enabled": False,
537
+ "hooks": ["on_request", "on_response"],
538
+ },
539
+ ]
540
+
541
+ # Try to load actual plugin registry
542
+ try:
543
+ from praisonaiagents.plugins import get_plugin_registry
544
+ registry = get_plugin_registry()
545
+ if registry:
546
+ # Merge with actual plugins
547
+ pass
548
+ except ImportError:
549
+ pass
550
+
551
+ return plugins
552
+
553
+
554
+ def _get_config_path():
555
+ """Get config file path."""
556
+ from pathlib import Path
557
+
558
+ config_dir = Path.home() / ".praisonai"
559
+ config_dir.mkdir(exist_ok=True)
560
+ return config_dir / "config.json"
561
+
562
+
563
+ def _load_config(path):
564
+ """Load config from file."""
565
+ import json
566
+
567
+ if path.exists():
568
+ with open(path) as f:
569
+ return json.load(f)
570
+ return {}
571
+
572
+
573
+ def _save_config(path, config):
574
+ """Save config to file."""
575
+ import json
576
+
577
+ with open(path, "w") as f:
578
+ json.dump(config, f, indent=2)
579
+
580
+
581
+ @app.callback(invoke_without_command=True)
582
+ def plugins_callback(ctx: typer.Context):
583
+ """Show plugins help if no subcommand provided."""
584
+ if ctx.invoked_subcommand is None:
585
+ help_text = """
586
+ [bold cyan]PraisonAI Plugins - Plugin Management[/bold cyan]
587
+
588
+ Manage plugins with: praisonai plugins <command>
589
+
590
+ [bold]Commands:[/bold]
591
+ [green]list[/green] List available plugins
592
+ [green]info[/green] Show plugin details
593
+ [green]enable[/green] Enable a plugin
594
+ [green]disable[/green] Disable a plugin
595
+ [green]doctor[/green] Check plugin health
596
+
597
+ [bold]Examples:[/bold]
598
+ praisonai plugins list
599
+ praisonai plugins info memory-core
600
+ praisonai plugins enable browser-tool
601
+ praisonai plugins doctor
602
+ """
603
+ try:
604
+ from rich import print as rprint
605
+ rprint(help_text)
606
+ except ImportError:
607
+ import re
608
+ plain = re.sub(r'\[/?[^\]]+\]', '', help_text)
609
+ print(plain)