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,529 @@
1
+ """
2
+ Schedule command group for PraisonAI CLI.
3
+
4
+ Provides scheduler management.
5
+ """
6
+
7
+ from typing import Optional
8
+
9
+ import typer
10
+
11
+ from ..output.console import get_output_controller
12
+
13
+ app = typer.Typer(help="Scheduler management")
14
+
15
+
16
+ def _run_schedule(args: list) -> int:
17
+ """Run schedule command with args."""
18
+ try:
19
+ from praisonai.cli.features.agent_scheduler import AgentSchedulerHandler
20
+
21
+ # Parse subcommand
22
+ if args and args[0] in ['start', 'list', 'stop', 'logs', 'restart', 'delete', 'describe', 'save', 'stop-all', 'stats']:
23
+ subcommand = args[0]
24
+ remaining = args[1:] if len(args) > 1 else []
25
+
26
+ # Create minimal args namespace
27
+ class Args:
28
+ pass
29
+
30
+ fake_args = Args()
31
+ return AgentSchedulerHandler.handle_daemon_command(subcommand, fake_args, remaining)
32
+
33
+ return 0
34
+ except ImportError as e:
35
+ output = get_output_controller()
36
+ output.print_error(f"Scheduler module not available: {e}")
37
+ return 4
38
+
39
+
40
+ @app.command("add")
41
+ def schedule_add_cmd(
42
+ name: str = typer.Argument(..., help="Schedule name (e.g. 'morning-hello')"),
43
+ schedule: str = typer.Option(..., "--schedule", "-s", help="When to run: 'hourly', 'daily', '*/30m', 'cron:0 9 * * *', 'at:2026-03-01T09:00', 'in 20 minutes'"),
44
+ message: str = typer.Option("", "--message", "-m", help="Prompt / reminder text"),
45
+ agent: str = typer.Option("", "--agent", "-a", help="Agent ID to execute this job (default: first registered agent)"),
46
+ deliver: str = typer.Option("", "--deliver", "-d", help="Delivery token: 'origin', 'telegram', 'all', or 'platform:chat_id[:thread_id]'"),
47
+ channel: str = typer.Option("", "--channel", help="[Legacy] Delivery platform: telegram, discord, slack, whatsapp"),
48
+ channel_id: str = typer.Option("", "--channel-id", help="[Legacy] Target chat/channel ID on the platform"),
49
+ session_id: str = typer.Option("", "--session-id", help="Session ID to preserve conversation context"),
50
+ pre_run: str = typer.Option("", "--pre-run", help="Cheap pre-run gate command: exit 0 + output => run (output seeds the prompt); non-zero => skip (no model tokens, no delivery)"),
51
+ condition: str = typer.Option("", "--condition", help="Natural-language / expression alias for the pre-run gate"),
52
+ json_output: bool = typer.Option(False, "--json", help="Output JSON"),
53
+ ):
54
+ """Add a job to the schedule store (with optional delivery target).
55
+
56
+ Examples:
57
+ praisonai schedule add "morning-hello" -s "cron:0 9 * * *" -m "say hello"
58
+ praisonai schedule add "news" -s daily -m "news summary" --deliver telegram
59
+ praisonai schedule add "report" -s hourly -m "status report" --deliver all
60
+ praisonai schedule add "tg-reminder" -s daily -m "check email" --agent support --channel telegram --channel-id 12345
61
+ praisonai schedule add "inbox-watch" -s "*/5m" -m "Summarise new emails" --pre-run "scripts/new_mail.sh" --deliver telegram
62
+ """
63
+ output = get_output_controller()
64
+ try:
65
+ from praisonaiagents.tools.schedule_tools import schedule_add as _schedule_add
66
+
67
+ # Build delivery target based on new or legacy format
68
+ delivery_kwargs = {}
69
+ if deliver:
70
+ # New token-based delivery
71
+ delivery_kwargs["deliver"] = deliver
72
+ elif channel or channel_id:
73
+ # Legacy explicit channel/channel_id
74
+ delivery_kwargs["channel"] = channel
75
+ delivery_kwargs["channel_id"] = channel_id
76
+
77
+ if session_id:
78
+ delivery_kwargs["session_id"] = session_id
79
+
80
+ result = _schedule_add(
81
+ name=name,
82
+ schedule=schedule,
83
+ message=message,
84
+ agent_id=agent,
85
+ **delivery_kwargs
86
+ )
87
+
88
+ # ``pre_run``/``condition`` run an arbitrary host shell command, so they
89
+ # are NOT part of the LLM-callable schedule_add surface. The CLI is a
90
+ # trusted, human-driven surface, so set them on the stored job here.
91
+ if (pre_run or condition) and "Error" not in result:
92
+ try:
93
+ from praisonaiagents.tools.schedule_tools import _get_store
94
+ store = _get_store()
95
+ job = store.get_by_name(name)
96
+ if job is not None:
97
+ job.pre_run = pre_run or None
98
+ job.condition = condition or None
99
+ store.update(job)
100
+ except Exception as e:
101
+ output.print_error(f"Failed to set pre-run gate: {e}")
102
+ raise typer.Exit(1)
103
+
104
+ if json_output:
105
+ import json as _json
106
+ print(_json.dumps({"result": result}))
107
+ else:
108
+ if "Error" in result:
109
+ output.print_error(result)
110
+ raise typer.Exit(1)
111
+ else:
112
+ output.print_success(result)
113
+ except ImportError as e:
114
+ output.print_error(f"Schedule tools not available: {e}")
115
+ raise typer.Exit(4)
116
+
117
+
118
+ @app.command("start")
119
+ def schedule_start(
120
+ agents_file: str = typer.Argument("agents.yaml", help="Agents YAML file"),
121
+ interval: Optional[str] = typer.Option(None, "--interval", "-i", help="Schedule interval (e.g., 'hourly', '*/30m')"),
122
+ daemon: bool = typer.Option(True, "--daemon/--no-daemon", help="Run as daemon"),
123
+ name: Optional[str] = typer.Option(None, "--name", "-n", help="Job name"),
124
+ ):
125
+ """Start scheduled agent execution."""
126
+ args = ["start", agents_file]
127
+ if interval:
128
+ args.extend(["--interval", interval])
129
+ if not daemon:
130
+ args.append("--no-daemon")
131
+ if name:
132
+ args.extend(["--name", name])
133
+
134
+ raise typer.Exit(_run_schedule(args))
135
+
136
+
137
+ @app.command("stop")
138
+ def schedule_stop(
139
+ job_id: Optional[str] = typer.Argument(None, help="Job ID to stop (or 'all')"),
140
+ ):
141
+ """Stop scheduled job(s)."""
142
+ if job_id == "all":
143
+ raise typer.Exit(_run_schedule(["stop-all"]))
144
+ elif job_id:
145
+ raise typer.Exit(_run_schedule(["stop", job_id]))
146
+ else:
147
+ raise typer.Exit(_run_schedule(["stop"]))
148
+
149
+
150
+ @app.command("list")
151
+ def schedule_list(
152
+ json_output: bool = typer.Option(False, "--json", help="Output JSON"),
153
+ ):
154
+ """List scheduled jobs."""
155
+ args = ["list"]
156
+ if json_output:
157
+ args.append("--json")
158
+ raise typer.Exit(_run_schedule(args))
159
+
160
+
161
+ @app.command("logs")
162
+ def schedule_logs(
163
+ job_id: Optional[str] = typer.Argument(None, help="Job ID"),
164
+ tail: int = typer.Option(50, "--tail", "-n", help="Number of lines"),
165
+ follow: bool = typer.Option(False, "--follow", "-f", help="Follow log output"),
166
+ ):
167
+ """View scheduler logs."""
168
+ args = ["logs"]
169
+ if job_id:
170
+ args.append(job_id)
171
+ args.extend(["--tail", str(tail)])
172
+ if follow:
173
+ args.append("--follow")
174
+ raise typer.Exit(_run_schedule(args))
175
+
176
+
177
+ @app.command("restart")
178
+ def schedule_restart(
179
+ job_id: str = typer.Argument(..., help="Job ID to restart"),
180
+ ):
181
+ """Restart a scheduled job."""
182
+ raise typer.Exit(_run_schedule(["restart", job_id]))
183
+
184
+
185
+ @app.command("delete")
186
+ def schedule_delete(
187
+ job_id: str = typer.Argument(..., help="Job ID to delete"),
188
+ confirm: bool = typer.Option(False, "--yes", "-y", help="Skip confirmation"),
189
+ ):
190
+ """Delete a scheduled job."""
191
+ if not confirm:
192
+ confirmed = typer.confirm(f"Delete job {job_id}?")
193
+ if not confirmed:
194
+ output = get_output_controller()
195
+ output.print_info("Cancelled")
196
+ raise typer.Exit(0)
197
+
198
+ raise typer.Exit(_run_schedule(["delete", job_id]))
199
+
200
+
201
+ @app.command("describe")
202
+ def schedule_describe(
203
+ job_id: str = typer.Argument(..., help="Job ID"),
204
+ json_output: bool = typer.Option(False, "--json", help="Output JSON"),
205
+ ):
206
+ """Show job details."""
207
+ args = ["describe", job_id]
208
+ if json_output:
209
+ args.append("--json")
210
+ raise typer.Exit(_run_schedule(args))
211
+
212
+
213
+ @app.command("stats")
214
+ def schedule_stats(
215
+ json_output: bool = typer.Option(False, "--json", help="Output JSON"),
216
+ ):
217
+ """Show scheduler statistics."""
218
+ args = ["stats"]
219
+ if json_output:
220
+ args.append("--json")
221
+ raise typer.Exit(_run_schedule(args))
222
+
223
+
224
+ # ── Blueprint commands ─────────────────────────────────────────────────
225
+
226
+
227
+ @app.command("blueprint")
228
+ def schedule_blueprint(
229
+ blueprint_name: str = typer.Argument(..., help="Blueprint name (morning-brief, important-mail, weekly-review)"),
230
+ hour: Optional[int] = typer.Option(None, "--hour", help="Delivery hour (0-23)"),
231
+ minute: Optional[int] = typer.Option(None, "--minute", help="Delivery minute (0-59)"),
232
+ weekdays: Optional[str] = typer.Option(None, "--weekdays", help="Days: mon-fri, daily, weekends, or a single day"),
233
+ focus: Optional[str] = typer.Option(None, "--focus", help="Focus area"),
234
+ interval: Optional[int] = typer.Option(None, "--interval", help="Interval in minutes (for interval-based blueprints)"),
235
+ keywords: Optional[str] = typer.Option(None, "--keywords", help="Priority keywords (for important-mail)"),
236
+ deliver: str = typer.Option("", "--deliver", "-d", help="Delivery target"),
237
+ agent: str = typer.Option("", "--agent", "-a", help="Agent ID"),
238
+ json_output: bool = typer.Option(False, "--json", help="Output JSON"),
239
+ ):
240
+ """Create a schedule from a blueprint template.
241
+
242
+ Examples:
243
+ praisonai schedule blueprint morning-brief --hour 8 --weekdays mon-fri --deliver telegram
244
+ praisonai schedule blueprint important-mail --interval 30 --keywords urgent,deadline --deliver telegram
245
+ praisonai schedule blueprint weekly-review --hour 17 --weekdays fri --focus tech
246
+ """
247
+ output = get_output_controller()
248
+ try:
249
+ from praisonai.scheduler.blueprint_catalogue import BlueprintCatalogue
250
+ from praisonaiagents.tools.schedule_tools import schedule_add as _schedule_add
251
+
252
+ catalogue = BlueprintCatalogue()
253
+ bp = catalogue.get_blueprint(blueprint_name)
254
+ if bp is None:
255
+ available = [b.name for b in catalogue.list_blueprints()]
256
+ output.print_error(
257
+ f"Blueprint '{blueprint_name}' not found. "
258
+ f"Available: {', '.join(available)}"
259
+ )
260
+ raise typer.Exit(1)
261
+
262
+ # Build slots dict from CLI args
263
+ cli_slot_map: dict = {
264
+ "hour": hour, "minute": minute, "weekdays": weekdays,
265
+ "focus": focus, "interval_minutes": interval, "keywords": keywords,
266
+ }
267
+ slots = {}
268
+ for slot in bp.slots:
269
+ cli_val = cli_slot_map.get(slot.name)
270
+ if cli_val is not None:
271
+ slots[slot.name] = cli_val
272
+
273
+ resolved = catalogue.resolve_slots(bp, slots)
274
+ prompt = catalogue.materialize_prompt(bp, resolved)
275
+ schedule_expr = catalogue.materialize_schedule(bp, resolved)
276
+ final_deliver = deliver or bp.default_deliver
277
+
278
+ result = _schedule_add(
279
+ name=blueprint_name,
280
+ schedule=schedule_expr,
281
+ message=prompt,
282
+ deliver=final_deliver,
283
+ agent_id=agent or bp.default_agent,
284
+ )
285
+
286
+ if json_output:
287
+ import json as _json
288
+ print(_json.dumps({"result": result, "blueprint": blueprint_name,
289
+ "schedule": schedule_expr}))
290
+ else:
291
+ output.print_success(result)
292
+ except typer.Exit:
293
+ raise
294
+ except Exception as e:
295
+ output.print_error(str(e))
296
+ raise typer.Exit(1)
297
+
298
+
299
+ @app.command("blueprint-list")
300
+ def schedule_blueprint_list(
301
+ category: Optional[str] = typer.Option(None, "--category", "-c", help="Filter by category"),
302
+ json_output: bool = typer.Option(False, "--json", help="Output JSON"),
303
+ ):
304
+ """List available blueprints."""
305
+ output = get_output_controller()
306
+ try:
307
+ from praisonai.scheduler.blueprint_catalogue import BlueprintCatalogue
308
+
309
+ catalogue = BlueprintCatalogue()
310
+ blueprints = catalogue.list_blueprints(category=category)
311
+
312
+ if json_output:
313
+ import json as _json
314
+ print(_json.dumps([{
315
+ "name": bp.name,
316
+ "description": bp.description,
317
+ "category": bp.category,
318
+ "tags": bp.tags,
319
+ "slots": [
320
+ {"name": s.name, "type": s.type, "label": s.label,
321
+ "default": s.default, "choices": s.choices}
322
+ for s in bp.slots
323
+ ],
324
+ "default_deliver": bp.default_deliver,
325
+ "builtin": bp.builtin,
326
+ } for bp in blueprints]))
327
+ else:
328
+ if not blueprints:
329
+ output.print_info("No blueprints found.")
330
+ else:
331
+ output.print_header(f"Available blueprints ({len(blueprints)}):")
332
+ for bp in blueprints:
333
+ slot_desc = ", ".join(
334
+ f"{s.name}={s.default}" if s.default is not None else s.name
335
+ for s in bp.slots
336
+ )
337
+ tag = " [builtin]" if bp.builtin else " [custom]"
338
+ output.print_info(f" {bp.name} [{bp.category}]{tag} — {bp.description}")
339
+ output.print_info(f" Slots: {slot_desc}")
340
+ if bp.default_deliver:
341
+ output.print_info(f" Default delivery: {bp.default_deliver}")
342
+ except Exception as e:
343
+ output.print_error(str(e))
344
+ raise typer.Exit(1)
345
+
346
+
347
+ # ── Suggestion commands ─────────────────────────────────────────────────
348
+
349
+
350
+ @app.command("suggestions")
351
+ def schedule_suggestions(
352
+ json_output: bool = typer.Option(False, "--json", help="Output JSON"),
353
+ ):
354
+ """List pending automation suggestions."""
355
+ output = get_output_controller()
356
+ try:
357
+ from praisonai.scheduler.suggestion_engine import SuggestionEngine
358
+
359
+ engine = SuggestionEngine()
360
+ pending = engine.pending()
361
+
362
+ if json_output:
363
+ import json as _json
364
+ print(_json.dumps([{
365
+ "id": s.id,
366
+ "blueprint_name": s.blueprint_name,
367
+ "slots": s.slots,
368
+ "reason": s.reason,
369
+ "created_at": s.created_at,
370
+ "expires_at": s.expires_at,
371
+ } for s in pending]))
372
+ else:
373
+ if not pending:
374
+ output.print_info("No pending suggestions.")
375
+ else:
376
+ output.print_header(f"Suggestions ({len(pending)}):")
377
+ for s in pending:
378
+ slot_str = ", ".join(f"{k}={v}" for k, v in s.slots.items())
379
+ output.print_info(f" [{s.id}] Blueprint: {s.blueprint_name}")
380
+ output.print_info(f" Reason: {s.reason or 'N/A'}")
381
+ output.print_info(f" Slots: {slot_str or '(defaults)'}")
382
+ output.print_info(f" Accept: praisonai schedule suggestion-accept {s.id}")
383
+ output.print_info(f" Dismiss: praisonai schedule suggestion-dismiss {s.id}")
384
+ except Exception as e:
385
+ output.print_error(str(e))
386
+ raise typer.Exit(1)
387
+
388
+
389
+ @app.command("suggestion-accept")
390
+ def schedule_suggestion_accept(
391
+ suggestion_id: str = typer.Argument(..., help="Suggestion ID to accept"),
392
+ deliver: str = typer.Option("", "--deliver", "-d", help="Override delivery target"),
393
+ ):
394
+ """Accept a suggestion and create the schedule job."""
395
+ output = get_output_controller()
396
+ try:
397
+ from praisonai.scheduler.suggestion_engine import SuggestionEngine
398
+ from praisonai.scheduler.blueprint_catalogue import BlueprintCatalogue
399
+ from praisonaiagents.tools.schedule_tools import schedule_add as _schedule_add
400
+
401
+ engine = SuggestionEngine()
402
+ sug = engine.get_suggestion(suggestion_id)
403
+ if sug is None or sug.dismissed or sug.accepted:
404
+ output.print_error(f"Suggestion '{suggestion_id}' not found or already handled.")
405
+ raise typer.Exit(1)
406
+
407
+ # Reject expired suggestions (expires_at == 0 means no expiry)
408
+ import time as _time
409
+ if sug.expires_at != 0 and sug.expires_at <= _time.time():
410
+ output.print_error(f"Suggestion '{suggestion_id}' has expired.")
411
+ raise typer.Exit(1)
412
+
413
+ catalogue = BlueprintCatalogue()
414
+ bp = catalogue.get_blueprint(sug.blueprint_name)
415
+ if bp is None:
416
+ output.print_error(f"Blueprint '{sug.blueprint_name}' for suggestion not found.")
417
+ raise typer.Exit(1)
418
+
419
+ resolved = catalogue.resolve_slots(bp, sug.slots)
420
+ prompt = catalogue.materialize_prompt(bp, resolved)
421
+ schedule_expr = catalogue.materialize_schedule(bp, resolved)
422
+ final_deliver = deliver or sug.deliver or bp.default_deliver
423
+
424
+ result = _schedule_add(
425
+ name=sug.blueprint_name,
426
+ schedule=schedule_expr,
427
+ message=prompt,
428
+ deliver=final_deliver,
429
+ accept_suggestion=suggestion_id,
430
+ )
431
+
432
+ if result.startswith("Error") or "already exists" in result:
433
+ output.print_error(result)
434
+ raise typer.Exit(1)
435
+ output.print_success(f"Suggestion accepted. {result}")
436
+ except typer.Exit:
437
+ raise
438
+ except Exception as e:
439
+ output.print_error(str(e))
440
+ raise typer.Exit(1)
441
+
442
+
443
+ @app.command("suggestion-dismiss")
444
+ def schedule_suggestion_dismiss(
445
+ suggestion_id: str = typer.Argument(..., help="Suggestion ID to dismiss"),
446
+ ):
447
+ """Dismiss a suggestion without creating a job."""
448
+ output = get_output_controller()
449
+ try:
450
+ from praisonai.scheduler.suggestion_engine import SuggestionEngine
451
+ engine = SuggestionEngine()
452
+ ok = engine.dismiss(suggestion_id)
453
+ if ok:
454
+ output.print_info(f"Suggestion '{suggestion_id}' dismissed.")
455
+ else:
456
+ output.print_error(f"Suggestion '{suggestion_id}' not found.")
457
+ raise typer.Exit(1)
458
+ except typer.Exit:
459
+ raise
460
+ except Exception as e:
461
+ output.print_error(str(e))
462
+ raise typer.Exit(1)
463
+
464
+
465
+ @app.command("suggestion-propose")
466
+ def schedule_suggestion_propose(
467
+ blueprint_name: str = typer.Argument(..., help="Blueprint to suggest"),
468
+ reason: str = typer.Option("", "--reason", "-r", help="Why this is being suggested"),
469
+ hour: Optional[int] = typer.Option(None, "--hour"),
470
+ minute: Optional[int] = typer.Option(None, "--minute"),
471
+ weekdays: Optional[str] = typer.Option(None, "--weekdays"),
472
+ focus: Optional[str] = typer.Option(None, "--focus"),
473
+ interval: Optional[int] = typer.Option(None, "--interval"),
474
+ keywords: Optional[str] = typer.Option(None, "--keywords", help="Priority keywords (for important-mail)"),
475
+ deliver: str = typer.Option("", "--deliver", "-d", help="Suggested delivery target"),
476
+ ):
477
+ """Propose a blueprint as a suggestion (manual/CLI trigger)."""
478
+ output = get_output_controller()
479
+ try:
480
+ from praisonai.scheduler.blueprint_catalogue import BlueprintCatalogue
481
+ from praisonai.scheduler.suggestion_engine import SuggestionEngine
482
+
483
+ catalogue = BlueprintCatalogue()
484
+ bp = catalogue.get_blueprint(blueprint_name)
485
+ if bp is None:
486
+ available = [b.name for b in catalogue.list_blueprints()]
487
+ output.print_error(
488
+ f"Blueprint '{blueprint_name}' not found. "
489
+ f"Available: {', '.join(available)}"
490
+ )
491
+ raise typer.Exit(1)
492
+
493
+ cli_slot_map: dict = {
494
+ "hour": hour, "minute": minute, "weekdays": weekdays,
495
+ "focus": focus, "interval_minutes": interval,
496
+ "keywords": keywords,
497
+ }
498
+ slots = {}
499
+ for slot in bp.slots:
500
+ val = cli_slot_map.get(slot.name)
501
+ if val is not None:
502
+ slots[slot.name] = val
503
+
504
+ engine = SuggestionEngine()
505
+ sug_id = engine.propose(
506
+ blueprint_name=blueprint_name,
507
+ slots=slots,
508
+ deliver=deliver,
509
+ reason=reason or f"Suggestion from CLI for {blueprint_name}",
510
+ )
511
+
512
+ if sug_id:
513
+ output.print_success(f"Suggestion created (id: {sug_id}).")
514
+ output.print_info(f" Accept: praisonai schedule suggestion-accept {sug_id}")
515
+ output.print_info(f" Dismiss: praisonai schedule suggestion-dismiss {sug_id}")
516
+ else:
517
+ output.print_warning("Suggestion not created (cap reached or duplicate).")
518
+ except typer.Exit:
519
+ raise
520
+ except Exception as e:
521
+ output.print_error(str(e))
522
+ raise typer.Exit(1)
523
+
524
+
525
+ @app.callback(invoke_without_command=True)
526
+ def schedule_callback(ctx: typer.Context):
527
+ """Show schedule help or list jobs."""
528
+ if ctx.invoked_subcommand is None:
529
+ schedule_list(json_output=False)