praisonai-code 0.0.30__tar.gz → 0.0.32__tar.gz

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 (300) hide show
  1. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/PKG-INFO +1 -1
  2. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/__init__.py +1 -1
  3. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/approval_backend.py +138 -38
  4. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/run.py +4 -0
  5. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/configuration/credentials.py +39 -12
  6. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/setup/handler.py +61 -0
  7. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code.egg-info/PKG-INFO +1 -1
  8. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/pyproject.toml +1 -1
  9. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/README.md +0 -0
  10. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/__main__.py +0 -0
  11. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/_bot_bridge.py +0 -0
  12. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/_framework_availability.py +0 -0
  13. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/_logging.py +0 -0
  14. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/_registry.py +0 -0
  15. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/_safe_loader.py +0 -0
  16. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/_version.py +0 -0
  17. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/_wrapper_bridge.py +0 -0
  18. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/__init__.py +0 -0
  19. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/_forward_shim.py +0 -0
  20. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/_paths.py +0 -0
  21. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/_warnings.py +0 -0
  22. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/_wrapper_reexport.py +0 -0
  23. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/app.py +0 -0
  24. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/branding.py +0 -0
  25. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/__init__.py +0 -0
  26. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/acp.py +0 -0
  27. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/agent.py +0 -0
  28. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/agents.py +0 -0
  29. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/attach.py +0 -0
  30. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/auth.py +0 -0
  31. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/benchmark.py +0 -0
  32. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/browser.py +0 -0
  33. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/call.py +0 -0
  34. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/chat.py +0 -0
  35. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/checkpoint.py +0 -0
  36. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/code.py +0 -0
  37. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/command.py +0 -0
  38. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/commit.py +0 -0
  39. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/completion.py +0 -0
  40. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/config.py +0 -0
  41. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/daemon.py +0 -0
  42. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/debug.py +0 -0
  43. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/deploy.py +0 -0
  44. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/diag.py +0 -0
  45. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/doctor.py +0 -0
  46. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/endpoints.py +0 -0
  47. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/environment.py +0 -0
  48. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/eval.py +0 -0
  49. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/github.py +0 -0
  50. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/hooks.py +0 -0
  51. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/init.py +0 -0
  52. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/loop.py +0 -0
  53. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/lsp.py +0 -0
  54. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/memory.py +0 -0
  55. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/models.py +0 -0
  56. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/obs.py +0 -0
  57. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/package.py +0 -0
  58. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/paths.py +0 -0
  59. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/permissions.py +0 -0
  60. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/plugins.py +0 -0
  61. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/port.py +0 -0
  62. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/publish.py +0 -0
  63. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/registry.py +0 -0
  64. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/research.py +0 -0
  65. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/retrieval.py +0 -0
  66. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/rules.py +0 -0
  67. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/sandbox.py +0 -0
  68. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/serve.py +0 -0
  69. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/session.py +0 -0
  70. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/setup.py +0 -0
  71. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/skills.py +0 -0
  72. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/templates.py +0 -0
  73. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/test.py +0 -0
  74. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/todo.py +0 -0
  75. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/tools.py +0 -0
  76. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/traces.py +0 -0
  77. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/tracker.py +0 -0
  78. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/ui.py +0 -0
  79. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/up.py +0 -0
  80. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/version.py +0 -0
  81. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/commands/workflow.py +0 -0
  82. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/config_loader.py +0 -0
  83. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/configuration/__init__.py +0 -0
  84. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/configuration/config.schema.json +0 -0
  85. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/configuration/loader.py +0 -0
  86. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/configuration/model_resolver.py +0 -0
  87. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/configuration/oauth.py +0 -0
  88. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/configuration/paths.py +0 -0
  89. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/configuration/resolver.py +0 -0
  90. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/configuration/schema.py +0 -0
  91. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/execution/__init__.py +0 -0
  92. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/execution/core.py +0 -0
  93. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/execution/profiler.py +0 -0
  94. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/execution/request.py +0 -0
  95. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/execution/result.py +0 -0
  96. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/fallback_schema.py +0 -0
  97. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/__init__.py +0 -0
  98. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/_approval_bridge.py +0 -0
  99. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/_endpoint_registry.py +0 -0
  100. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/_search_registry.py +0 -0
  101. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/action_orchestrator.py +0 -0
  102. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/agents.py +0 -0
  103. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/at_mentions.py +0 -0
  104. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/audit_cli.py +0 -0
  105. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/auto_memory.py +0 -0
  106. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/auto_mode.py +0 -0
  107. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/autonomy_mode.py +0 -0
  108. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/base.py +0 -0
  109. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/benchmark.py +0 -0
  110. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/capabilities.py +0 -0
  111. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/checkpoints.py +0 -0
  112. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/cli_profiler.py +0 -0
  113. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/code_intelligence.py +0 -0
  114. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/compaction.py +0 -0
  115. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/compare.py +0 -0
  116. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/config_hierarchy.py +0 -0
  117. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/context_manager.py +0 -0
  118. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/cost_tracker.py +0 -0
  119. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/csv_test_runner.py +0 -0
  120. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/custom_definitions.py +0 -0
  121. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/debug.py +0 -0
  122. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/diag.py +0 -0
  123. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/display_jsonl.py +0 -0
  124. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/doctor/__init__.py +0 -0
  125. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/doctor/checks/__init__.py +0 -0
  126. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/doctor/checks/_wrapper_checks.py +0 -0
  127. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/doctor/checks/acp_checks.py +0 -0
  128. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/doctor/checks/bot_checks.py +0 -0
  129. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/doctor/checks/config_checks.py +0 -0
  130. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/doctor/checks/db_checks.py +0 -0
  131. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/doctor/checks/env_checks.py +0 -0
  132. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/doctor/checks/gateway_checks.py +0 -0
  133. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/doctor/checks/lsp_checks.py +0 -0
  134. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/doctor/checks/mcp_checks.py +0 -0
  135. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/doctor/checks/memory_checks.py +0 -0
  136. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/doctor/checks/network_checks.py +0 -0
  137. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/doctor/checks/obs_checks.py +0 -0
  138. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/doctor/checks/packaging_checks.py +0 -0
  139. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/doctor/checks/performance_checks.py +0 -0
  140. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/doctor/checks/permissions_checks.py +0 -0
  141. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/doctor/checks/runtime_checks.py +0 -0
  142. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/doctor/checks/runtime_migration_checks.py +0 -0
  143. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/doctor/checks/selftest_checks.py +0 -0
  144. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/doctor/checks/serve_checks.py +0 -0
  145. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/doctor/checks/skills_checks.py +0 -0
  146. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/doctor/checks/tools_checks.py +0 -0
  147. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/doctor/engine.py +0 -0
  148. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/doctor/formatters.py +0 -0
  149. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/doctor/handler.py +0 -0
  150. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/doctor/models.py +0 -0
  151. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/doctor/registry.py +0 -0
  152. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/examples.py +0 -0
  153. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/external_agents.py +0 -0
  154. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/fast_context.py +0 -0
  155. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/file_history.py +0 -0
  156. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/flow_display.py +0 -0
  157. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/git_attribution.py +0 -0
  158. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/git_integration.py +0 -0
  159. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/guardrail.py +0 -0
  160. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/handoff.py +0 -0
  161. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/hooks.py +0 -0
  162. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/hybrid_workflow.py +0 -0
  163. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/image.py +0 -0
  164. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/interactive_core_headless.py +0 -0
  165. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/interactive_runtime.py +0 -0
  166. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/interactive_test_harness.py +0 -0
  167. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/interactive_tools.py +0 -0
  168. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/interactive_tui.py +0 -0
  169. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/job_workflow.py +0 -0
  170. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/jobs.py +0 -0
  171. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/knowledge.py +0 -0
  172. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/knowledge_cli.py +0 -0
  173. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/lite.py +0 -0
  174. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/logs.py +0 -0
  175. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/lsp_cli.py +0 -0
  176. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/lsp_diagnostics.py +0 -0
  177. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/mcp.py +0 -0
  178. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/message_queue.py +0 -0
  179. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/metrics.py +0 -0
  180. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/migrate.py +0 -0
  181. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/migration_flow.py +0 -0
  182. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/migration_spec.py +0 -0
  183. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/n8n.py +0 -0
  184. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/observability.py +0 -0
  185. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/output_modes.py +0 -0
  186. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/output_style.py +0 -0
  187. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/package.py +0 -0
  188. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/performance.py +0 -0
  189. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/profiler/__init__.py +0 -0
  190. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/profiler/core.py +0 -0
  191. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/profiler/optimizations.py +0 -0
  192. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/profiler/suite.py +0 -0
  193. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/queue/__init__.py +0 -0
  194. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/queue/manager.py +0 -0
  195. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/queue/models.py +0 -0
  196. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/queue/persistence.py +0 -0
  197. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/queue/scheduler.py +0 -0
  198. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/queue/worker.py +0 -0
  199. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/recipe_creator.py +0 -0
  200. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/recipe_prompts.py +0 -0
  201. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/repo_map.py +0 -0
  202. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/router.py +0 -0
  203. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/safe_shell.py +0 -0
  204. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/sandbox_executor.py +0 -0
  205. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/sdk_knowledge.py +0 -0
  206. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/session.py +0 -0
  207. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/session_checkpoints.py +0 -0
  208. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/setup/__init__.py +0 -0
  209. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/setup/templates.py +0 -0
  210. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/skills.py +0 -0
  211. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/slash_commands.py +0 -0
  212. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/telemetry.py +0 -0
  213. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/thinking.py +0 -0
  214. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/todo.py +0 -0
  215. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/tools.py +0 -0
  216. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/tui/__init__.py +0 -0
  217. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/tui/cli.py +0 -0
  218. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/tui/config.py +0 -0
  219. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/tui/debug.py +0 -0
  220. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/tui/events.py +0 -0
  221. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/tui/mock_provider.py +0 -0
  222. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/tui/orchestrator.py +0 -0
  223. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/tui/screens/__init__.py +0 -0
  224. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/tui/screens/help.py +0 -0
  225. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/tui/screens/main.py +0 -0
  226. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/tui/screens/queue.py +0 -0
  227. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/tui/screens/session.py +0 -0
  228. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/tui/screens/settings.py +0 -0
  229. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/tui/session_store.py +0 -0
  230. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/tui/widgets/__init__.py +0 -0
  231. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/tui/widgets/chat.py +0 -0
  232. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/tui/widgets/command_popup.py +0 -0
  233. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/tui/widgets/composer.py +0 -0
  234. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/tui/widgets/file_popup.py +0 -0
  235. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/tui/widgets/queue_panel.py +0 -0
  236. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/tui/widgets/status.py +0 -0
  237. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/tui/widgets/tool_panel.py +0 -0
  238. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/wizard.py +0 -0
  239. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/workflow.py +0 -0
  240. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/features/yaml_utils.py +0 -0
  241. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/interactive/__init__.py +0 -0
  242. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/interactive/async_tui.py +0 -0
  243. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/interactive/config.py +0 -0
  244. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/interactive/core.py +0 -0
  245. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/interactive/events.py +0 -0
  246. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/interactive/frontends/__init__.py +0 -0
  247. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/interactive/frontends/rich_frontend.py +0 -0
  248. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/interactive/frontends/textual_frontend.py +0 -0
  249. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/interactive/praison_io.py +0 -0
  250. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/interactive/repl.py +0 -0
  251. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/interactive/split_tui.py +0 -0
  252. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/interactive/tui_app.py +0 -0
  253. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/legacy/__init__.py +0 -0
  254. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/legacy/env_security.py +0 -0
  255. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/legacy/praison_ai.py +0 -0
  256. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/legacy/prompt_dispatch.py +0 -0
  257. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/main.py +0 -0
  258. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/output/__init__.py +0 -0
  259. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/output/console.py +0 -0
  260. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/output/event_bridge.py +0 -0
  261. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/schedule_cli.py +0 -0
  262. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/schema_provider.py +0 -0
  263. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/session/__init__.py +0 -0
  264. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/session/resume.py +0 -0
  265. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/session/unified.py +0 -0
  266. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/state/__init__.py +0 -0
  267. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/state/identifiers.py +0 -0
  268. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/state/project_sessions.py +0 -0
  269. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/state/sessions.py +0 -0
  270. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/ui/__init__.py +0 -0
  271. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/ui/config.py +0 -0
  272. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/ui/events.py +0 -0
  273. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/ui/mg_backend.py +0 -0
  274. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/ui/plain.py +0 -0
  275. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/ui/rich_backend.py +0 -0
  276. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/unified_schema.py +0 -0
  277. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/utils/env_utils.py +0 -0
  278. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/utils/project.py +0 -0
  279. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli/utils/stdin.py +0 -0
  280. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli_backends/__init__.py +0 -0
  281. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli_backends/claude.py +0 -0
  282. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/cli_backends/registry.py +0 -0
  283. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/llm/__init__.py +0 -0
  284. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/llm/catalogue.py +0 -0
  285. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/llm/config.py +0 -0
  286. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/llm/credentials.py +0 -0
  287. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/llm/env.py +0 -0
  288. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/runtime/__init__.py +0 -0
  289. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/runtime/__main__.py +0 -0
  290. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/runtime/client.py +0 -0
  291. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/runtime/descriptor.py +0 -0
  292. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/runtime/server.py +0 -0
  293. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/tool_registry.py +0 -0
  294. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code/tool_resolver.py +0 -0
  295. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code.egg-info/SOURCES.txt +0 -0
  296. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code.egg-info/dependency_links.txt +0 -0
  297. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code.egg-info/entry_points.txt +0 -0
  298. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code.egg-info/requires.txt +0 -0
  299. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/praisonai_code.egg-info/top_level.txt +0 -0
  300. {praisonai_code-0.0.30 → praisonai_code-0.0.32}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: praisonai-code
3
- Version: 0.0.30
3
+ Version: 0.0.32
4
4
  Summary: Agentic terminal CLI for PraisonAI — the terminal-native agent (run, chat, code, runtime, cli backends) extracted from the praisonai wrapper.
5
5
  Author: Mervin Praison
6
6
  License: MIT
@@ -12,6 +12,6 @@ the remaining terminal-agent modules, with PEP 562 shims left behind at the
12
12
  old ``praisonai.*`` paths for backward compatibility.
13
13
  """
14
14
 
15
- __version__ = "0.0.30"
15
+ __version__ = "0.0.32"
16
16
 
17
17
  __all__ = ["__version__"]
@@ -87,17 +87,20 @@ class InteractiveCLIApprovalBackend:
87
87
 
88
88
  return f"{tool_name}({args_display})"
89
89
 
90
- def _prompt_user(self, request: ApprovalRequest) -> tuple[bool, bool]:
90
+ def _prompt_user(self, request: ApprovalRequest) -> tuple[bool, bool, str, Optional[str]]:
91
91
  """
92
92
  Prompt user for approval decision.
93
-
93
+
94
94
  Returns:
95
- Tuple of (approved, persist_as_always)
95
+ Tuple of ``(approved, persist_as_always, scope, deny_reason)`` where
96
+ ``scope`` is ``"command"`` (narrow, command-prefix) or ``"tool"``
97
+ (explicit blanket ``tool:*``) and ``deny_reason`` is optional
98
+ steering feedback captured on denial (``None`` when absent).
96
99
  """
97
100
  if self.non_interactive:
98
101
  # In non-interactive mode, we're only here if the check returned ASK
99
102
  # (ALLOW/DENY are handled in request_approval), so default to deny
100
- return (False, False)
103
+ return (False, False, "command", None)
101
104
 
102
105
  # Interactive prompt
103
106
  tool_display = self._format_tool_call(request)
@@ -113,10 +116,21 @@ class InteractiveCLIApprovalBackend:
113
116
  print(f"Risk: {request.risk_level}")
114
117
  if request.agent_name:
115
118
  print(f"Agent: {request.agent_name}")
116
-
119
+
120
+ # Show a preview of the actual change for file-mutating tools so the
121
+ # user approves the concrete change, not just a tool name.
122
+ preview = self._render_preview(request)
123
+ if preview:
124
+ print(preview)
125
+
126
+ # Compute the narrow pattern once so the user can see exactly what a
127
+ # persisted "always" will grant.
128
+ narrow_pattern = self._create_pattern(request, scope="command")
129
+
117
130
  print("\nOptions:")
118
131
  print(" [a] Allow once")
119
- print(" [A] Always allow (persist rule)")
132
+ print(f" [A] Always allow this command ({narrow_pattern})")
133
+ print(f" [T] Always allow ALL uses of {request.tool_name} ({request.tool_name}:*)")
120
134
  print(" [d] Deny")
121
135
  print(" [D] Always deny (persist rule)")
122
136
 
@@ -125,41 +139,117 @@ class InteractiveCLIApprovalBackend:
125
139
  choice = input("\nYour choice: ").strip()
126
140
 
127
141
  if choice == "a":
128
- return (True, False)
142
+ return (True, False, "command", None)
129
143
  elif choice == "A":
130
- return (True, True)
144
+ return (True, True, "command", None)
145
+ elif choice == "T":
146
+ return (True, True, "tool", None)
131
147
  elif choice == "d":
132
- return (False, False)
148
+ return (False, False, "command", self._prompt_deny_reason())
133
149
  elif choice == "D":
134
- return (False, True)
150
+ return (False, True, "command", self._prompt_deny_reason())
135
151
  else:
136
- print("Invalid choice. Please enter a, A, d, or D.")
152
+ print("Invalid choice. Please enter a, A, T, d, or D.")
137
153
  except (EOFError, KeyboardInterrupt):
138
154
  print("\n\nApproval cancelled.")
139
- return (False, False)
155
+ return (False, False, "command", None)
156
+
157
+ def _prompt_deny_reason(self) -> Optional[str]:
158
+ """Optionally capture a denial reason to steer the agent.
159
+
160
+ Returns the trimmed reason string, or ``None`` if the user provides no
161
+ feedback (blank input) or input is unavailable.
162
+ """
163
+ try:
164
+ reason = input("Reason for denial (optional, steers the agent): ").strip()
165
+ except (EOFError, KeyboardInterrupt):
166
+ return None
167
+ return reason or None
168
+
169
+ def _render_preview(self, request: ApprovalRequest) -> Optional[str]:
170
+ """Render a change preview for file-mutating tools.
171
+
172
+ For ``edit``/``apply_patch`` a unified diff is shown when available;
173
+ for ``write`` the content (truncated) is shown. Returns ``None`` when
174
+ there is nothing meaningful to preview.
175
+ """
176
+ tool_name = request.tool_name
177
+ args = request.arguments or {}
178
+ if tool_name not in ("edit", "write", "apply_patch"):
179
+ return None
180
+
181
+ # Prefer an already-computed diff if the caller supplied one.
182
+ diff = args.get("diff") or args.get("patch")
183
+ if isinstance(diff, str) and diff.strip():
184
+ return f"\nPreview (diff):\n{diff}"
185
+
186
+ if tool_name == "write":
187
+ content = args.get("content") or args.get("text")
188
+ path = args.get("path") or args.get("file_path") or ""
189
+ if isinstance(content, str):
190
+ shown = content if len(content) <= 2000 else content[:2000] + "\n... (truncated)"
191
+ header = f" to {path}" if path else ""
192
+ return f"\nPreview (content{header}):\n{shown}"
193
+
194
+ return None
140
195
 
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
196
+ def _create_pattern(self, request: ApprovalRequest, scope: str = "command") -> str:
197
+ """Create a permission pattern from the request.
198
+
199
+ Derives the *narrowest reasonable* persisted pattern so that approving a
200
+ single command does not silently grant unrestricted use of a tool.
201
+
202
+ Args:
203
+ request: The approval request.
204
+ scope: ``"command"`` (default) derives a command-prefix scope for
205
+ shell tools (e.g. ``bash:git status *``) and a per-argument
206
+ scope for other tools. ``"tool"`` is the explicit, clearly
207
+ labelled "allow all uses of this tool" choice which emits the
208
+ blanket ``tool_name:*`` pattern.
209
+
210
+ Returns:
211
+ A permission glob pattern. For ``scope="command"`` the result is
212
+ **never** the blanket ``tool_name:*``; malformed, missing, or
213
+ oversized arguments fall back to a literal (single-use) target so
214
+ the persisted rule can only match the exact invocation the user
215
+ approved, preserving a fail-closed posture. Only the explicit
216
+ ``scope="tool"`` choice emits ``tool_name:*``.
217
+ """
145
218
  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}:*"
219
+
220
+ # Explicit, clearly-labelled "always allow all uses of this tool" only.
221
+ if scope == "tool":
222
+ return f"{tool_name}:*"
223
+
224
+ # Shell tools: derive a reusable command-prefix scope via the shared
225
+ # core helper so declarative (YAML/--allow) and interactive rules scope
226
+ # identically. Unknown/compound commands stay literal (fail-closed).
227
+ if tool_name in ("bash", "shell") and "command" in request.arguments:
228
+ command = request.arguments["command"]
229
+ if isinstance(command, str) and command.strip():
230
+ from praisonaiagents.permissions import derive_pattern
231
+
232
+ return derive_pattern(f"{tool_name}:{command}")
233
+ # Missing/blank/non-string command: fall back to a literal,
234
+ # single-use target rather than the blanket ``tool:*`` grant.
235
+ return f"{tool_name}:{command}"
236
+
237
+ # Non-shell tools: scope to the first argument value when it is a short
238
+ # string, otherwise fall back to a literal single-use target. This never
239
+ # over-grants beyond the specific invocation the user approved (and, on
240
+ # the deny path, never over-denies the entire tool).
241
+ if request.arguments:
242
+ first_arg = next(iter(request.arguments.values()), "")
243
+ if isinstance(first_arg, str) and first_arg and len(first_arg) < 100:
244
+ return f"{tool_name}:{first_arg}"
245
+ # Non-string / empty / oversized first argument: keep it literal so
246
+ # the rule matches only this exact value, not every use of the tool.
247
+ return f"{tool_name}:{first_arg}"
248
+
249
+ # No arguments at all: match only the bare (argument-less) invocation,
250
+ # never the wildcard. ``scope="tool"`` above remains the sole path to
251
+ # ``tool_name:*``.
252
+ return f"{tool_name}:"
163
253
 
164
254
  def _decision_from_mode(self, request: ApprovalRequest) -> Optional[ApprovalDecision]:
165
255
  """
@@ -237,13 +327,13 @@ class InteractiveCLIApprovalBackend:
237
327
 
238
328
  # Need to ask - run prompt in thread to avoid blocking
239
329
  loop = asyncio.get_running_loop()
240
- approved, persist = await loop.run_in_executor(
330
+ approved, persist, scope, deny_reason = await loop.run_in_executor(
241
331
  None, self._prompt_user, request
242
332
  )
243
333
 
244
334
  # If user wants to persist the decision
245
335
  if persist:
246
- pattern = self._create_pattern(request)
336
+ pattern = self._create_pattern(request, scope=scope)
247
337
  action = PermissionAction.ALLOW if approved else PermissionAction.DENY
248
338
 
249
339
  # Create and add the rule
@@ -255,12 +345,22 @@ class InteractiveCLIApprovalBackend:
255
345
  priority=100, # User rules have high priority
256
346
  )
257
347
  self.permission_manager.add_rule(rule)
258
-
348
+
349
+ # Surface any denial feedback so the agent can steer instead of just
350
+ # aborting. A denied approval with a reason becomes a correction.
351
+ metadata: Dict[str, Any] = {"persistent": persist}
352
+ if not approved and deny_reason:
353
+ metadata["feedback"] = deny_reason
354
+
355
+ reason_text = "User decision" + (" (persisted)" if persist else "")
356
+ if not approved and deny_reason:
357
+ reason_text = f"User denied: {deny_reason}"
358
+
259
359
  return ApprovalDecision(
260
360
  approved=approved,
261
- reason="User decision" + (" (persisted)" if persist else ""),
361
+ reason=reason_text,
262
362
  approver="user",
263
- metadata={"persistent": persist}
363
+ metadata=metadata,
264
364
  )
265
365
 
266
366
  def set_permission_mode(self, mode: PermissionMode):
@@ -1195,6 +1195,10 @@ def _run_prompt(
1195
1195
  )
1196
1196
 
1197
1197
  # Add session support to Agent if needed
1198
+ # NOTE: build_cli_memory_config / apply_cli_session_continuity are
1199
+ # imported above from ..state.project_sessions. Do NOT re-import them
1200
+ # from ..utils.project here — that stale version lacks the auto_save
1201
+ # kwarg and would shadow the correct implementation.
1198
1202
  memory_cfg = build_cli_memory_config(session_id=session_id, auto_save=auto_save_name)
1199
1203
  if memory_cfg is not None:
1200
1204
  agent_config["memory"] = memory_cfg
@@ -56,42 +56,69 @@ class ProviderCredential:
56
56
  class CredentialStore:
57
57
  """
58
58
  Secure credential storage with atomic writes and proper permissions.
59
-
60
- Stores credentials in ~/.praison/credentials.json with 0o600 permissions
61
- for security. All write operations are atomic to prevent corruption.
59
+
60
+ Stores credentials in ~/.praisonai/credentials.json with 0o600
61
+ permissions for security. All write operations are atomic to prevent
62
+ corruption.
63
+
64
+ The legacy ~/.praison/credentials.json location is kept as a read-only
65
+ backward-compatibility fallback: if the canonical file does not yet exist
66
+ but the legacy one does, the legacy file is read so existing users keep
67
+ working during the deprecation window. Writes always target the canonical
68
+ location so credentials converge on a single source of truth.
62
69
  """
63
-
70
+
64
71
  def __init__(self, credentials_path: Optional[Path] = None):
65
72
  """
66
73
  Initialize credential store.
67
-
74
+
68
75
  Args:
69
76
  credentials_path: Optional custom path for credentials file.
70
- Defaults to ~/.praison/credentials.json
77
+ Defaults to ~/.praisonai/credentials.json
71
78
  """
72
79
  if credentials_path:
73
80
  self.credentials_path = credentials_path
81
+ self.legacy_credentials_path: Optional[Path] = None
74
82
  else:
75
83
  home = Path.home()
76
- self.credentials_path = home / ".praison" / "credentials.json"
84
+ self.credentials_path = home / ".praisonai" / "credentials.json"
85
+ self.legacy_credentials_path = home / ".praison" / "credentials.json"
86
+
87
+ def _effective_read_path(self) -> Path:
88
+ """Return the path to read credentials from.
89
+
90
+ Prefers the canonical location; only falls back to the legacy
91
+ ~/.praison/credentials.json when the canonical file is absent.
92
+ """
93
+ if self.credentials_path.exists():
94
+ return self.credentials_path
95
+ legacy = self.legacy_credentials_path
96
+ if legacy is not None and legacy.exists():
97
+ return legacy
98
+ return self.credentials_path
77
99
 
78
100
  def _ensure_directory_exists(self) -> None:
79
101
  """Ensure parent directory exists, creating it if necessary."""
80
102
  self.credentials_path.parent.mkdir(parents=True, exist_ok=True)
81
103
 
82
104
  def _read_credentials(self) -> Dict[str, Dict[str, Any]]:
83
- """Read and parse credentials file."""
105
+ """Read and parse credentials file.
106
+
107
+ Reads from the canonical path when present, otherwise transparently
108
+ falls back to the legacy ~/.praison/credentials.json location.
109
+ """
84
110
  try:
85
- if not self.credentials_path.exists():
111
+ read_path = self._effective_read_path()
112
+ if not read_path.exists():
86
113
  return {}
87
114
 
88
115
  # Check file permissions for security
89
- file_stat = self.credentials_path.stat()
116
+ file_stat = read_path.stat()
90
117
  if file_stat.st_mode & 0o077: # Check if group/other have any permissions
91
118
  # Fix permissions automatically
92
- os.chmod(self.credentials_path, 0o600)
119
+ os.chmod(read_path, 0o600)
93
120
 
94
- with open(self.credentials_path, 'r') as f:
121
+ with open(read_path, 'r') as f:
95
122
  return json.load(f)
96
123
  except (json.JSONDecodeError, FileNotFoundError, PermissionError):
97
124
  # Return empty dict on any read error
@@ -128,6 +128,7 @@ class SetupHandler(CommandHandler):
128
128
 
129
129
  self._save_env(praison_home, env_vars, output)
130
130
  self._save_config(praison_home, config, output)
131
+ self._save_credentials(praison_home, provider, env_key, api_key, model, output)
131
132
 
132
133
  output.print_success("Setup complete")
133
134
  output.console.print(f"Configuration saved to {praison_home}")
@@ -243,6 +244,14 @@ class SetupHandler(CommandHandler):
243
244
 
244
245
  self._save_env(praison_home, env_vars, output)
245
246
  self._save_config(praison_home, config, output)
247
+ self._save_credentials(
248
+ praison_home,
249
+ provider_id,
250
+ env_key,
251
+ env_vars.get(env_key) if env_key else None,
252
+ model,
253
+ output,
254
+ )
246
255
 
247
256
  if create_starter:
248
257
  self._create_starter_yaml(output, model)
@@ -493,6 +502,58 @@ class SetupHandler(CommandHandler):
493
502
  config_file.chmod(0o600)
494
503
  output.print_success(f"Configuration saved to {config_file}")
495
504
 
505
+ def _save_credentials(
506
+ self,
507
+ praison_home: Path,
508
+ provider: Optional[str],
509
+ env_key: Optional[str],
510
+ api_key: Optional[str],
511
+ model: Optional[str],
512
+ output,
513
+ ) -> None:
514
+ """Mirror the API key into the unified CredentialStore.
515
+
516
+ Historically ``setup`` only wrote keys to ``~/.praisonai/.env`` while
517
+ ``auth`` / ``inject_credentials_into_env`` read from a separate
518
+ ``CredentialStore``. Writing here too gives both code paths a single
519
+ source of truth so a key set via ``setup`` is picked up everywhere.
520
+
521
+ To stay a single source of truth, this writes to the *same* store that
522
+ ``auth`` / ``inject_credentials_into_env`` read from. When
523
+ ``PRAISONAI_HOME`` is not overridden we use the default
524
+ ``CredentialStore()`` so its legacy ``~/.praison/credentials.json``
525
+ fallback is honoured and any pre-existing ``auth`` credentials are
526
+ transparently migrated onto the canonical file on this write (no data
527
+ loss). Only when ``PRAISONAI_HOME`` points somewhere other than the
528
+ default home do we anchor the store under that directory.
529
+ """
530
+ # Only providers with a real API key can be stored; local providers
531
+ # (e.g. ollama) and empty keys are skipped silently.
532
+ if not provider or not env_key or not api_key:
533
+ return
534
+
535
+ try:
536
+ from ...configuration.credentials import CredentialStore
537
+
538
+ # Prefer the default store (canonical path + legacy fallback and
539
+ # migration) so setup and the auth/inject read paths converge on a
540
+ # single file. Fall back to an explicit path only when a custom
541
+ # PRAISONAI_HOME is in effect.
542
+ default_home = Path.home() / ".praisonai"
543
+ if praison_home.resolve() == default_home.resolve():
544
+ store = CredentialStore()
545
+ else:
546
+ store = CredentialStore(praison_home / "credentials.json")
547
+ store.store_credential(
548
+ provider=provider,
549
+ api_key=api_key,
550
+ model=model,
551
+ )
552
+ except Exception as e:
553
+ # Never fail setup because the credential mirror failed; the .env
554
+ # file has already been written as the primary artifact.
555
+ output.print_warning(f"Could not save to credential store: {e}")
556
+
496
557
  def _create_starter_yaml(self, output, model: str) -> None:
497
558
  """Create a starter agents.yaml file in the current directory."""
498
559
  agents_file = Path.cwd() / "agents.yaml"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: praisonai-code
3
- Version: 0.0.30
3
+ Version: 0.0.32
4
4
  Summary: Agentic terminal CLI for PraisonAI — the terminal-native agent (run, chat, code, runtime, cli backends) extracted from the praisonai wrapper.
5
5
  Author: Mervin Praison
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "praisonai-code"
3
- version = "0.0.30"
3
+ version = "0.0.32"
4
4
  description = "Agentic terminal CLI for PraisonAI — the terminal-native agent (run, chat, code, runtime, cli backends) extracted from the praisonai wrapper."
5
5
  readme = "README.md"
6
6
  license = {text = "MIT"}