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,272 @@
1
+ """
2
+ Interactive CLI approval backend for PraisonAI.
3
+
4
+ Provides interactive prompts for tool approval with persistent rules.
5
+ """
6
+
7
+ import asyncio
8
+ import os
9
+ from typing import Optional, Dict, Any
10
+
11
+ from praisonaiagents.approval import ApprovalRequest, ApprovalDecision
12
+ from praisonaiagents.permissions import (
13
+ PermissionManager,
14
+ PermissionRule,
15
+ PermissionAction,
16
+ PermissionMode,
17
+ )
18
+
19
+
20
+ class InteractiveCLIApprovalBackend:
21
+ """
22
+ Interactive CLI backend for tool approvals.
23
+
24
+ Provides terminal prompts with options to:
25
+ - Allow once
26
+ - Always allow (persist as rule)
27
+ - Deny
28
+
29
+ Integrates with PermissionManager for persistent project-scoped rules.
30
+ """
31
+
32
+ def __init__(
33
+ self,
34
+ project_dir: Optional[str] = None,
35
+ permission_mode: PermissionMode = PermissionMode.DEFAULT,
36
+ non_interactive: bool = False,
37
+ permissions_config: Optional[Dict[str, Any]] = None,
38
+ ):
39
+ """
40
+ Initialize the interactive CLI approval backend.
41
+
42
+ Args:
43
+ project_dir: Project directory for scoped permissions
44
+ permission_mode: Permission mode (default, accept_edits, dont_ask, bypass, plan)
45
+ non_interactive: Whether to auto-approve/deny without prompting
46
+ permissions_config: Declarative permission rules from YAML/CLI/Python
47
+ """
48
+ self.project_dir = project_dir or os.getcwd()
49
+ self.permission_mode = permission_mode
50
+ self.non_interactive = non_interactive
51
+
52
+ # Initialize permission manager with project-scoped storage
53
+ permissions_dir = os.path.join(self.project_dir, ".praisonai", "permissions")
54
+ self.permission_manager = PermissionManager(storage_dir=permissions_dir)
55
+
56
+ # Load declarative permissions if provided
57
+ if permissions_config:
58
+ self.permission_manager.load_rules_from_config(permissions_config, priority_base=75)
59
+
60
+ def _build_target_string(self, request: ApprovalRequest) -> str:
61
+ """Build target string for permission check."""
62
+ if "command" in request.arguments:
63
+ return f"{request.tool_name}:{request.arguments['command']}"
64
+ elif request.arguments:
65
+ # Include first arg value for more specific matching
66
+ first_arg = next(iter(request.arguments.values()), "")
67
+ if isinstance(first_arg, str) and len(first_arg) < 100:
68
+ return f"{request.tool_name}:{first_arg}"
69
+ # Use wildcard for consistency with permission patterns
70
+ return f"{request.tool_name}:*"
71
+
72
+ def _format_tool_call(self, request: ApprovalRequest) -> str:
73
+ """Format a tool call for display."""
74
+ tool_name = request.tool_name
75
+ args = request.arguments
76
+
77
+ # Format arguments nicely
78
+ if args:
79
+ arg_strs = []
80
+ for key, value in args.items():
81
+ if isinstance(value, str) and len(value) > 50:
82
+ value = value[:47] + "..."
83
+ arg_strs.append(f"{key}={repr(value)}")
84
+ args_display = ", ".join(arg_strs)
85
+ else:
86
+ args_display = ""
87
+
88
+ return f"{tool_name}({args_display})"
89
+
90
+ def _prompt_user(self, request: ApprovalRequest) -> tuple[bool, bool]:
91
+ """
92
+ Prompt user for approval decision.
93
+
94
+ Returns:
95
+ Tuple of (approved, persist_as_always)
96
+ """
97
+ if self.non_interactive:
98
+ # In non-interactive mode, we're only here if the check returned ASK
99
+ # (ALLOW/DENY are handled in request_approval), so default to deny
100
+ return (False, False)
101
+
102
+ # Interactive prompt
103
+ tool_display = self._format_tool_call(request)
104
+ risk_color = {
105
+ "critical": "\033[91m", # Red
106
+ "high": "\033[93m", # Yellow
107
+ "medium": "\033[96m", # Cyan
108
+ "low": "\033[92m", # Green
109
+ }.get(request.risk_level, "\033[0m")
110
+
111
+ print(f"\n{risk_color}⚠ Tool Approval Required{chr(27)}[0m")
112
+ print(f"Tool: {tool_display}")
113
+ print(f"Risk: {request.risk_level}")
114
+ if request.agent_name:
115
+ print(f"Agent: {request.agent_name}")
116
+
117
+ print("\nOptions:")
118
+ print(" [a] Allow once")
119
+ print(" [A] Always allow (persist rule)")
120
+ print(" [d] Deny")
121
+ print(" [D] Always deny (persist rule)")
122
+
123
+ while True:
124
+ try:
125
+ choice = input("\nYour choice: ").strip()
126
+
127
+ if choice == "a":
128
+ return (True, False)
129
+ elif choice == "A":
130
+ return (True, True)
131
+ elif choice == "d":
132
+ return (False, False)
133
+ elif choice == "D":
134
+ return (False, True)
135
+ else:
136
+ print("Invalid choice. Please enter a, A, d, or D.")
137
+ except (EOFError, KeyboardInterrupt):
138
+ print("\n\nApproval cancelled.")
139
+ return (False, False)
140
+
141
+ def _create_pattern(self, request: ApprovalRequest) -> str:
142
+ """Create a permission pattern from the request."""
143
+ # Create a pattern that matches this specific tool
144
+ # Can be made more sophisticated to support wildcards
145
+ tool_name = request.tool_name
146
+
147
+ # For certain tools, we can create broader patterns
148
+ if tool_name == "bash" and "command" in request.arguments:
149
+ cmd = request.arguments["command"]
150
+ # Extract first word as command type
151
+ cmd_parts = cmd.split()
152
+ if cmd_parts:
153
+ first_word = cmd_parts[0]
154
+ # For git commands, create pattern for all git commands
155
+ if first_word == "git":
156
+ return f"{tool_name}:git *"
157
+ # For safe commands, broader patterns
158
+ elif first_word in ["ls", "pwd", "echo", "cat", "grep"]:
159
+ return f"{tool_name}:{first_word} *"
160
+
161
+ # Default to exact tool name
162
+ return f"{tool_name}:*"
163
+
164
+ def _decision_from_mode(self, request: ApprovalRequest) -> Optional[ApprovalDecision]:
165
+ """
166
+ Check if permission mode provides an immediate decision.
167
+
168
+ Returns None if mode doesn't apply, otherwise returns the decision.
169
+ """
170
+ # BYPASS mode: unconditional allow
171
+ if self.permission_mode == PermissionMode.BYPASS:
172
+ return ApprovalDecision(
173
+ approved=True,
174
+ reason="Bypassed by permission mode",
175
+ approver="permission_mode"
176
+ )
177
+
178
+ # PLAN mode: unconditional deny for write/edit/shell operations
179
+ if self.permission_mode == PermissionMode.PLAN:
180
+ if request.tool_name in ["write", "edit", "delete", "bash", "shell"]:
181
+ return ApprovalDecision(
182
+ approved=False,
183
+ reason="Blocked by plan mode",
184
+ approver="permission_mode"
185
+ )
186
+
187
+ # ACCEPT_EDITS mode: unconditional allow for edit/write operations
188
+ if self.permission_mode == PermissionMode.ACCEPT_EDITS:
189
+ if "edit" in request.tool_name.lower() or "write" in request.tool_name.lower():
190
+ return ApprovalDecision(
191
+ approved=True,
192
+ reason="Auto-approved edit by permission mode",
193
+ approver="permission_mode"
194
+ )
195
+
196
+ # DONT_ASK mode in non-interactive: deny all asks
197
+ if self.permission_mode == PermissionMode.DONT_ASK and self.non_interactive:
198
+ return ApprovalDecision(
199
+ approved=False,
200
+ reason="Denied by don't-ask mode",
201
+ approver="permission_mode"
202
+ )
203
+
204
+ return None
205
+
206
+ async def request_approval(self, request: ApprovalRequest) -> ApprovalDecision:
207
+ """
208
+ Request approval for a tool execution.
209
+
210
+ First checks permission modes, then existing rules, then prompts if needed.
211
+ """
212
+ # Check permission mode first (takes precedence)
213
+ mode_decision = self._decision_from_mode(request)
214
+ if mode_decision is not None:
215
+ return mode_decision
216
+
217
+ # Build target string for permission check
218
+ target = self._build_target_string(request)
219
+
220
+ # Check existing permissions
221
+ result = self.permission_manager.check(target, agent_name=request.agent_name)
222
+
223
+ if result.action == PermissionAction.ALLOW:
224
+ return ApprovalDecision(
225
+ approved=True,
226
+ reason="Allowed by rule",
227
+ approver="permission_rule",
228
+ metadata={"rule_id": result.rule.id if result.rule else None}
229
+ )
230
+ elif result.action == PermissionAction.DENY:
231
+ return ApprovalDecision(
232
+ approved=False,
233
+ reason="Denied by rule",
234
+ approver="permission_rule",
235
+ metadata={"rule_id": result.rule.id if result.rule else None}
236
+ )
237
+
238
+ # Need to ask - run prompt in thread to avoid blocking
239
+ loop = asyncio.get_running_loop()
240
+ approved, persist = await loop.run_in_executor(
241
+ None, self._prompt_user, request
242
+ )
243
+
244
+ # If user wants to persist the decision
245
+ if persist:
246
+ pattern = self._create_pattern(request)
247
+ action = PermissionAction.ALLOW if approved else PermissionAction.DENY
248
+
249
+ # Create and add the rule
250
+ rule = PermissionRule(
251
+ pattern=pattern,
252
+ action=action,
253
+ description=f"User {'allowed' if approved else 'denied'} {request.tool_name}",
254
+ agent_name=request.agent_name,
255
+ priority=100, # User rules have high priority
256
+ )
257
+ self.permission_manager.add_rule(rule)
258
+
259
+ return ApprovalDecision(
260
+ approved=approved,
261
+ reason="User decision" + (" (persisted)" if persist else ""),
262
+ approver="user",
263
+ metadata={"persistent": persist}
264
+ )
265
+
266
+ def set_permission_mode(self, mode: PermissionMode):
267
+ """Update the permission mode."""
268
+ self.permission_mode = mode
269
+
270
+ def get_permission_manager(self) -> PermissionManager:
271
+ """Get the underlying permission manager for direct access."""
272
+ return self.permission_manager
@@ -0,0 +1,94 @@
1
+ """
2
+ PraisonAI Branding - Single Source of Truth.
3
+
4
+ This module provides unified branding assets (logo, version, product name)
5
+ for all interactive UIs. All frontends should import from here.
6
+
7
+ Usage:
8
+ from praisonai.cli.branding import get_logo, get_version, PRODUCT_NAME
9
+ """
10
+
11
+ # Product name
12
+ PRODUCT_NAME = "Praison AI"
13
+
14
+ # ASCII Art Logos - responsive to terminal width
15
+ LOGO_LARGE = r"""
16
+ ██████╗ ██████╗ █████╗ ██╗███████╗ ██████╗ ███╗ ██╗ █████╗ ██╗
17
+ ██╔══██╗██╔══██╗██╔══██╗██║██╔════╝██╔═══██╗████╗ ██║ ██╔══██╗██║
18
+ ██████╔╝██████╔╝███████║██║███████╗██║ ██║██╔██╗ ██║ ███████║██║
19
+ ██╔═══╝ ██╔══██╗██╔══██║██║╚════██║██║ ██║██║╚██╗██║ ██╔══██║██║
20
+ ██║ ██║ ██║██║ ██║██║███████║╚██████╔╝██║ ╚████║ ██║ ██║██║
21
+ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝╚══════╝ ╚═════╝ ╚═╝ ╚═══╝ ╚═╝ ╚═╝╚═╝
22
+ """
23
+
24
+ LOGO_MEDIUM = r"""
25
+ ╔═╗┬─┐┌─┐┬┌─┐┌─┐┌┐┌ ╔═╗╦
26
+ ╠═╝├┬┘├─┤│└─┐│ ││││ ╠═╣║
27
+ ╩ ┴└─┴ ┴┴└─┘└─┘┘└┘ ╩ ╩╩
28
+ """
29
+
30
+ LOGO_SMALL = "▶ Praison AI"
31
+
32
+ LOGO_MINIMAL = "Praison AI"
33
+
34
+
35
+ def get_version() -> str:
36
+ """Get PraisonAI version string."""
37
+ try:
38
+ from praisonai import __version__
39
+ return __version__
40
+ except Exception:
41
+ return "1.0.0"
42
+
43
+
44
+ def get_logo(width: int = 80) -> str:
45
+ """
46
+ Get appropriate logo based on terminal width.
47
+
48
+ Args:
49
+ width: Terminal width in characters
50
+
51
+ Returns:
52
+ ASCII art logo string appropriate for the width
53
+ """
54
+ if width >= 75:
55
+ return LOGO_LARGE
56
+ elif width >= 40:
57
+ return LOGO_MEDIUM
58
+ else:
59
+ return LOGO_SMALL
60
+
61
+
62
+ def get_banner(width: int = 80, show_version: bool = True, model: str = None) -> str:
63
+ """
64
+ Get full banner with logo, version, and optional model info.
65
+
66
+ Args:
67
+ width: Terminal width
68
+ show_version: Whether to include version
69
+ model: Optional model name to display
70
+
71
+ Returns:
72
+ Complete banner string
73
+ """
74
+ lines = []
75
+
76
+ # Logo
77
+ logo = get_logo(width)
78
+ lines.append(logo.strip())
79
+ lines.append("")
80
+
81
+ # Version and model
82
+ version_line = f" v{get_version()}"
83
+ if model:
84
+ version_line += f" · Model: {model}"
85
+ lines.append(version_line)
86
+ lines.append("")
87
+
88
+ return "\n".join(lines)
89
+
90
+
91
+ def get_welcome_tips() -> str:
92
+ """Get welcome tips for new sessions."""
93
+ return """ Type your message and press Enter. Use /help for commands.
94
+ Use PageUp/PageDown or Ctrl+Up/Down to scroll."""
@@ -0,0 +1,114 @@
1
+ """
2
+ PraisonAI Code CLI Commands Module.
3
+
4
+ Agentic Typer command groups moved from ``praisonai.cli.commands`` as part
5
+ of the praisonai-code extraction (issue #2516 / parent #2512). Bot-channel
6
+ commands remain in ``praisonai.cli.commands``.
7
+
8
+ Lazy imports keep startup fast; each attribute maps to the ``app`` object of
9
+ the corresponding command module.
10
+ """
11
+
12
+ __all__ = [
13
+ 'run_app',
14
+ 'config_app',
15
+ 'traces_app',
16
+ 'env_app',
17
+ 'session_app',
18
+ 'schedule_app',
19
+ 'serve_app',
20
+ 'completion_app',
21
+ 'version_app',
22
+ 'debug_app',
23
+ 'lsp_app',
24
+ 'diag_app',
25
+ 'doctor_app',
26
+ 'setup_app',
27
+ 'acp_app',
28
+ 'mcp_app',
29
+ 'rag_app',
30
+ 'test_app',
31
+ 'examples_app',
32
+ 'replay_app',
33
+ 'github_app',
34
+ 'langextract_app',
35
+ 'agent_app',
36
+ 'command_app',
37
+ ]
38
+
39
+
40
+ def __getattr__(name: str):
41
+ """Lazy load command apps."""
42
+ if name == 'run_app':
43
+ from .run import app as run_app
44
+ return run_app
45
+ elif name == 'config_app':
46
+ from .config import app as config_app
47
+ return config_app
48
+ elif name == 'traces_app':
49
+ from .traces import app as traces_app
50
+ return traces_app
51
+ elif name == 'env_app':
52
+ from .environment import app as env_app
53
+ return env_app
54
+ elif name == 'session_app':
55
+ from .session import app as session_app
56
+ return session_app
57
+ elif name == 'schedule_app':
58
+ from .schedule import app as schedule_app
59
+ return schedule_app
60
+ elif name == 'serve_app':
61
+ from .serve import app as serve_app
62
+ return serve_app
63
+ elif name == 'completion_app':
64
+ from .completion import app as completion_app
65
+ return completion_app
66
+ elif name == 'version_app':
67
+ from .version import app as version_app
68
+ return version_app
69
+ elif name == 'debug_app':
70
+ from .debug import app as debug_app
71
+ return debug_app
72
+ elif name == 'lsp_app':
73
+ from .lsp import app as lsp_app
74
+ return lsp_app
75
+ elif name == 'diag_app':
76
+ from .diag import app as diag_app
77
+ return diag_app
78
+ elif name == 'doctor_app':
79
+ from .doctor import app as doctor_app
80
+ return doctor_app
81
+ elif name == 'setup_app':
82
+ from .setup import app as setup_app
83
+ return setup_app
84
+ elif name == 'acp_app':
85
+ from .acp import app as acp_app
86
+ return acp_app
87
+ elif name == 'mcp_app':
88
+ from .mcp import app as mcp_app
89
+ return mcp_app
90
+ elif name == 'rag_app':
91
+ from .rag import app as rag_app
92
+ return rag_app
93
+ elif name == 'test_app':
94
+ from .test import app as test_app
95
+ return test_app
96
+ elif name == 'examples_app':
97
+ from .examples import app as examples_app
98
+ return examples_app
99
+ elif name == 'replay_app':
100
+ from .replay import app as replay_app
101
+ return replay_app
102
+ elif name == 'github_app':
103
+ from .github import app as github_app
104
+ return github_app
105
+ elif name == 'langextract_app':
106
+ from .langextract import app as langextract_app
107
+ return langextract_app
108
+ elif name == 'agent_app':
109
+ from .agent import app as agent_app
110
+ return agent_app
111
+ elif name == 'command_app':
112
+ from .command import app as command_app
113
+ return command_app
114
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
@@ -0,0 +1,80 @@
1
+ """
2
+ ACP command group for PraisonAI CLI.
3
+
4
+ Wraps existing ACP functionality from features/acp.py.
5
+ Provides Agent Client Protocol server for IDE integration.
6
+ """
7
+
8
+ from typing import Optional
9
+
10
+ import typer
11
+
12
+ from ..output.console import get_output_controller
13
+
14
+ app = typer.Typer(help="Agent Client Protocol server")
15
+
16
+
17
+ @app.callback(invoke_without_command=True)
18
+ def acp_main(
19
+ ctx: typer.Context,
20
+ workspace: str = typer.Option(".", "--workspace", "-w", help="Workspace root directory"),
21
+ agent: str = typer.Option("default", "--agent", "-a", help="Agent name or configuration file"),
22
+ agents_file: Optional[str] = typer.Option(None, "--agents", help="Multi-agent configuration YAML file"),
23
+ router: bool = typer.Option(False, "--router", help="Enable router agent for task delegation"),
24
+ model: Optional[str] = typer.Option(None, "--model", "-m", help="LLM model to use"),
25
+ resume: Optional[str] = typer.Option(None, "--resume", "-r", help="Resume session by ID"),
26
+ last: bool = typer.Option(False, "--last", help="Resume the last session"),
27
+ approve: str = typer.Option("manual", "--approve", help="Approval mode: manual, auto, scoped"),
28
+ read_only: bool = typer.Option(True, "--read-only/--allow-write", help="Read-only mode"),
29
+ allow_shell: bool = typer.Option(False, "--allow-shell", help="Allow shell command execution"),
30
+ allow_network: bool = typer.Option(False, "--allow-network", help="Allow network requests"),
31
+ debug: bool = typer.Option(False, "--debug", help="Enable debug logging"),
32
+ profile: Optional[str] = typer.Option(None, "--profile", help="Use named profile from config"),
33
+ ):
34
+ """Start ACP server for IDE integration.
35
+
36
+ DEPRECATED: Use `praisonai serve acp` instead.
37
+ """
38
+ import sys
39
+
40
+ # Print deprecation warning
41
+ print("\n\033[93m⚠ DEPRECATION WARNING:\033[0m", file=sys.stderr)
42
+ print("\033[93m'praisonai acp' is deprecated and will be removed in a future version.\033[0m", file=sys.stderr)
43
+ print("\033[93mPlease use 'praisonai serve acp' instead.\033[0m\n", file=sys.stderr)
44
+
45
+ # Build args for existing handler
46
+ args = [
47
+ "--workspace", workspace,
48
+ "--agent", agent,
49
+ "--approve", approve,
50
+ ]
51
+
52
+ if agents_file:
53
+ args.extend(["--agents", agents_file])
54
+ if router:
55
+ args.append("--router")
56
+ if model:
57
+ args.extend(["--model", model])
58
+ if resume:
59
+ args.extend(["--resume", resume])
60
+ if last:
61
+ args.append("--last")
62
+ if not read_only:
63
+ args.append("--allow-write")
64
+ if allow_shell:
65
+ args.append("--allow-shell")
66
+ if allow_network:
67
+ args.append("--allow-network")
68
+ if debug:
69
+ args.append("--debug")
70
+ if profile:
71
+ args.extend(["--profile", profile])
72
+
73
+ try:
74
+ from praisonai.cli.features.acp import run_acp_command
75
+ exit_code = run_acp_command(args)
76
+ raise typer.Exit(exit_code)
77
+ except ImportError as e:
78
+ output = get_output_controller()
79
+ output.print_error(f"ACP module not available: {e}")
80
+ raise typer.Exit(4)
@@ -0,0 +1,116 @@
1
+ """
2
+ Agent management commands for PraisonAI CLI.
3
+
4
+ Provides commands to list and inspect custom agent definitions.
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="Manage custom agents")
14
+
15
+
16
+ @app.command()
17
+ def list(
18
+ verbose: bool = typer.Option(False, "--verbose", "-v", help="Show detailed information"),
19
+ ):
20
+ """List all discovered custom agents."""
21
+ output = get_output_controller()
22
+
23
+ try:
24
+ from praisonai.cli.features.custom_definitions import CustomDefinitionsDiscovery
25
+
26
+ discovery = CustomDefinitionsDiscovery()
27
+ discovery.discover()
28
+
29
+ agents = discovery.list_agents()
30
+
31
+ if not agents:
32
+ output.print_info("No custom agents found.")
33
+ output.print_info("Run 'praisonai init' to scaffold a starter .praisonai/ project,")
34
+ output.print_info("or create agents in .praisonai/agents/*.md or ~/.praisonai/agents/*.md")
35
+ return
36
+
37
+ from rich.table import Table
38
+ from rich.console import Console
39
+
40
+ console = Console()
41
+ table = Table(title="Custom Agents", show_header=True)
42
+ table.add_column("Name", style="cyan")
43
+ table.add_column("Source", style="yellow")
44
+ table.add_column("Model", style="green")
45
+
46
+ if verbose:
47
+ table.add_column("Path", style="dim")
48
+ table.add_column("Role", style="magenta")
49
+
50
+ for agent in agents:
51
+ row = [
52
+ agent.name,
53
+ agent.source,
54
+ agent.model or "default",
55
+ ]
56
+
57
+ if verbose:
58
+ row.extend([
59
+ str(agent.path),
60
+ agent.role or "Assistant"
61
+ ])
62
+
63
+ table.add_row(*row)
64
+
65
+ console.print(table)
66
+
67
+ except Exception as e:
68
+ output.print_error(str(e))
69
+ raise typer.Exit(1)
70
+
71
+
72
+ @app.command()
73
+ def show(
74
+ name: str = typer.Argument(help="Agent name to inspect"),
75
+ ):
76
+ """Show details of a specific agent."""
77
+ output = get_output_controller()
78
+
79
+ try:
80
+ from praisonai.cli.features.custom_definitions import CustomDefinitionsDiscovery
81
+
82
+ discovery = CustomDefinitionsDiscovery()
83
+ agent = discovery.get_agent(name)
84
+
85
+ if not agent:
86
+ output.print_error(f"Agent '{name}' not found")
87
+ raise typer.Exit(1)
88
+
89
+ from rich.console import Console
90
+ from rich.panel import Panel
91
+ from rich.syntax import Syntax
92
+
93
+ console = Console()
94
+
95
+ # Build agent info
96
+ info = f"""[bold cyan]Agent: {agent.name}[/bold cyan]
97
+ [yellow]Source:[/yellow] {agent.source}
98
+ [yellow]Path:[/yellow] {agent.path}
99
+ [yellow]Model:[/yellow] {agent.model or 'default'}
100
+ [yellow]Role:[/yellow] {agent.role or 'Assistant'}
101
+ [yellow]Goal:[/yellow] {agent.goal or 'N/A'}"""
102
+
103
+ console.print(Panel(info, title="Agent Details", border_style="cyan"))
104
+
105
+ if agent.instructions or agent.system_prompt:
106
+ prompt_text = agent.instructions or agent.system_prompt
107
+ syntax = Syntax(prompt_text, "markdown", theme="monokai", line_numbers=False)
108
+ console.print(Panel(syntax, title="System Prompt", border_style="green"))
109
+
110
+ if agent.tools:
111
+ tools_text = "\n".join(f"- {tool}" for tool in agent.tools)
112
+ console.print(Panel(tools_text, title="Tools", border_style="yellow"))
113
+
114
+ except Exception as e:
115
+ output.print_error(str(e))
116
+ raise typer.Exit(1)