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.
- praisonai_code/__init__.py +17 -0
- praisonai_code/cli/__init__.py +12 -0
- praisonai_code/cli/_forward_shim.py +10 -0
- praisonai_code/cli/_paths.py +88 -0
- praisonai_code/cli/_warnings.py +58 -0
- praisonai_code/cli/app.py +757 -0
- praisonai_code/cli/approval_backend.py +272 -0
- praisonai_code/cli/branding.py +94 -0
- praisonai_code/cli/commands/__init__.py +114 -0
- praisonai_code/cli/commands/acp.py +80 -0
- praisonai_code/cli/commands/agent.py +116 -0
- praisonai_code/cli/commands/agents.py +80 -0
- praisonai_code/cli/commands/app.py +139 -0
- praisonai_code/cli/commands/attach.py +95 -0
- praisonai_code/cli/commands/audit.py +102 -0
- praisonai_code/cli/commands/auth.py +508 -0
- praisonai_code/cli/commands/batch.py +848 -0
- praisonai_code/cli/commands/benchmark.py +286 -0
- praisonai_code/cli/commands/browser.py +299 -0
- praisonai_code/cli/commands/call.py +45 -0
- praisonai_code/cli/commands/chat.py +332 -0
- praisonai_code/cli/commands/checkpoint.py +170 -0
- praisonai_code/cli/commands/code.py +276 -0
- praisonai_code/cli/commands/command.py +114 -0
- praisonai_code/cli/commands/commit.py +47 -0
- praisonai_code/cli/commands/completion.py +333 -0
- praisonai_code/cli/commands/config.py +681 -0
- praisonai_code/cli/commands/context.py +414 -0
- praisonai_code/cli/commands/daemon.py +203 -0
- praisonai_code/cli/commands/debug.py +142 -0
- praisonai_code/cli/commands/deploy.py +71 -0
- praisonai_code/cli/commands/diag.py +55 -0
- praisonai_code/cli/commands/docs.py +1575 -0
- praisonai_code/cli/commands/doctor.py +332 -0
- praisonai_code/cli/commands/endpoints.py +51 -0
- praisonai_code/cli/commands/environment.py +179 -0
- praisonai_code/cli/commands/eval.py +131 -0
- praisonai_code/cli/commands/examples.py +953 -0
- praisonai_code/cli/commands/flow.py +436 -0
- praisonai_code/cli/commands/github.py +752 -0
- praisonai_code/cli/commands/hooks.py +74 -0
- praisonai_code/cli/commands/init.py +174 -0
- praisonai_code/cli/commands/knowledge.py +440 -0
- praisonai_code/cli/commands/langextract.py +120 -0
- praisonai_code/cli/commands/langfuse.py +984 -0
- praisonai_code/cli/commands/loop.py +211 -0
- praisonai_code/cli/commands/lsp.py +112 -0
- praisonai_code/cli/commands/managed.py +659 -0
- praisonai_code/cli/commands/mcp.py +763 -0
- praisonai_code/cli/commands/memory.py +298 -0
- praisonai_code/cli/commands/models.py +264 -0
- praisonai_code/cli/commands/n8n.py +326 -0
- praisonai_code/cli/commands/obs.py +19 -0
- praisonai_code/cli/commands/package.py +76 -0
- praisonai_code/cli/commands/paths.py +106 -0
- praisonai_code/cli/commands/permissions.py +272 -0
- praisonai_code/cli/commands/plugins.py +609 -0
- praisonai_code/cli/commands/port.py +530 -0
- praisonai_code/cli/commands/profile.py +466 -0
- praisonai_code/cli/commands/publish.py +193 -0
- praisonai_code/cli/commands/rag.py +913 -0
- praisonai_code/cli/commands/realtime.py +52 -0
- praisonai_code/cli/commands/recipe.py +684 -0
- praisonai_code/cli/commands/registry.py +59 -0
- praisonai_code/cli/commands/replay.py +830 -0
- praisonai_code/cli/commands/research.py +49 -0
- praisonai_code/cli/commands/retrieval.py +377 -0
- praisonai_code/cli/commands/rules.py +71 -0
- praisonai_code/cli/commands/run.py +1573 -0
- praisonai_code/cli/commands/sandbox.py +371 -0
- praisonai_code/cli/commands/schedule.py +529 -0
- praisonai_code/cli/commands/serve.py +690 -0
- praisonai_code/cli/commands/session.py +450 -0
- praisonai_code/cli/commands/setup.py +174 -0
- praisonai_code/cli/commands/skills.py +545 -0
- praisonai_code/cli/commands/standardise.py +711 -0
- praisonai_code/cli/commands/templates.py +54 -0
- praisonai_code/cli/commands/test.py +558 -0
- praisonai_code/cli/commands/todo.py +74 -0
- praisonai_code/cli/commands/tools.py +205 -0
- praisonai_code/cli/commands/traces.py +145 -0
- praisonai_code/cli/commands/tracker.py +852 -0
- praisonai_code/cli/commands/train.py +613 -0
- praisonai_code/cli/commands/ui.py +172 -0
- praisonai_code/cli/commands/up.py +354 -0
- praisonai_code/cli/commands/validate.py +291 -0
- praisonai_code/cli/commands/version.py +101 -0
- praisonai_code/cli/commands/workflow.py +97 -0
- praisonai_code/cli/config_loader.py +437 -0
- praisonai_code/cli/configuration/__init__.py +27 -0
- praisonai_code/cli/configuration/config.schema.json +57 -0
- praisonai_code/cli/configuration/credentials.py +446 -0
- praisonai_code/cli/configuration/loader.py +364 -0
- praisonai_code/cli/configuration/model_resolver.py +161 -0
- praisonai_code/cli/configuration/oauth.py +389 -0
- praisonai_code/cli/configuration/paths.py +224 -0
- praisonai_code/cli/configuration/resolver.py +687 -0
- praisonai_code/cli/configuration/schema.py +317 -0
- praisonai_code/cli/execution/__init__.py +99 -0
- praisonai_code/cli/execution/core.py +208 -0
- praisonai_code/cli/execution/profiler.py +898 -0
- praisonai_code/cli/execution/request.py +85 -0
- praisonai_code/cli/execution/result.py +74 -0
- praisonai_code/cli/fallback_schema.py +416 -0
- praisonai_code/cli/features/__init__.py +278 -0
- praisonai_code/cli/features/_endpoint_registry.py +64 -0
- praisonai_code/cli/features/_search_registry.py +43 -0
- praisonai_code/cli/features/acp.py +236 -0
- praisonai_code/cli/features/action_orchestrator.py +576 -0
- praisonai_code/cli/features/agent_scheduler.py +773 -0
- praisonai_code/cli/features/agent_tools.py +603 -0
- praisonai_code/cli/features/agents.py +397 -0
- praisonai_code/cli/features/at_mentions.py +471 -0
- praisonai_code/cli/features/audit_cli.py +270 -0
- praisonai_code/cli/features/auto_memory.py +182 -0
- praisonai_code/cli/features/auto_mode.py +552 -0
- praisonai_code/cli/features/autonomy_mode.py +546 -0
- praisonai_code/cli/features/background.py +356 -0
- praisonai_code/cli/features/base.py +168 -0
- praisonai_code/cli/features/benchmark.py +1462 -0
- praisonai_code/cli/features/capabilities.py +1326 -0
- praisonai_code/cli/features/checkpoints.py +345 -0
- praisonai_code/cli/features/cli_profiler.py +335 -0
- praisonai_code/cli/features/code_intelligence.py +666 -0
- praisonai_code/cli/features/compaction.py +294 -0
- praisonai_code/cli/features/compare.py +534 -0
- praisonai_code/cli/features/config_hierarchy.py +366 -0
- praisonai_code/cli/features/context_manager.py +597 -0
- praisonai_code/cli/features/cost_tracker.py +514 -0
- praisonai_code/cli/features/csv_test_runner.py +736 -0
- praisonai_code/cli/features/custom_definitions.py +790 -0
- praisonai_code/cli/features/debug.py +810 -0
- praisonai_code/cli/features/deploy.py +605 -0
- praisonai_code/cli/features/diag.py +289 -0
- praisonai_code/cli/features/display_jsonl.py +173 -0
- praisonai_code/cli/features/doctor/__init__.py +63 -0
- praisonai_code/cli/features/doctor/checks/__init__.py +29 -0
- praisonai_code/cli/features/doctor/checks/acp_checks.py +220 -0
- praisonai_code/cli/features/doctor/checks/bot_checks.py +340 -0
- praisonai_code/cli/features/doctor/checks/config_checks.py +373 -0
- praisonai_code/cli/features/doctor/checks/db_checks.py +366 -0
- praisonai_code/cli/features/doctor/checks/env_checks.py +637 -0
- praisonai_code/cli/features/doctor/checks/gateway_checks.py +387 -0
- praisonai_code/cli/features/doctor/checks/lsp_checks.py +231 -0
- praisonai_code/cli/features/doctor/checks/mcp_checks.py +367 -0
- praisonai_code/cli/features/doctor/checks/memory_checks.py +268 -0
- praisonai_code/cli/features/doctor/checks/network_checks.py +251 -0
- praisonai_code/cli/features/doctor/checks/obs_checks.py +328 -0
- praisonai_code/cli/features/doctor/checks/packaging_checks.py +422 -0
- praisonai_code/cli/features/doctor/checks/performance_checks.py +235 -0
- praisonai_code/cli/features/doctor/checks/permissions_checks.py +259 -0
- praisonai_code/cli/features/doctor/checks/runtime_checks.py +650 -0
- praisonai_code/cli/features/doctor/checks/runtime_migration_checks.py +220 -0
- praisonai_code/cli/features/doctor/checks/selftest_checks.py +322 -0
- praisonai_code/cli/features/doctor/checks/serve_checks.py +426 -0
- praisonai_code/cli/features/doctor/checks/skills_checks.py +327 -0
- praisonai_code/cli/features/doctor/checks/tools_checks.py +371 -0
- praisonai_code/cli/features/doctor/engine.py +266 -0
- praisonai_code/cli/features/doctor/formatters.py +377 -0
- praisonai_code/cli/features/doctor/handler.py +564 -0
- praisonai_code/cli/features/doctor/models.py +276 -0
- praisonai_code/cli/features/doctor/registry.py +239 -0
- praisonai_code/cli/features/endpoints.py +1016 -0
- praisonai_code/cli/features/eval.py +559 -0
- praisonai_code/cli/features/examples.py +707 -0
- praisonai_code/cli/features/external_agents.py +231 -0
- praisonai_code/cli/features/fast_context.py +410 -0
- praisonai_code/cli/features/file_history.py +320 -0
- praisonai_code/cli/features/flow_display.py +566 -0
- praisonai_code/cli/features/git_attribution.py +159 -0
- praisonai_code/cli/features/git_integration.py +651 -0
- praisonai_code/cli/features/guardrail.py +171 -0
- praisonai_code/cli/features/handoff.py +252 -0
- praisonai_code/cli/features/hooks.py +583 -0
- praisonai_code/cli/features/hybrid_workflow.py +391 -0
- praisonai_code/cli/features/image.py +384 -0
- praisonai_code/cli/features/interactive_core_headless.py +450 -0
- praisonai_code/cli/features/interactive_runtime.py +600 -0
- praisonai_code/cli/features/interactive_test_harness.py +537 -0
- praisonai_code/cli/features/interactive_tools.py +428 -0
- praisonai_code/cli/features/interactive_tui.py +603 -0
- praisonai_code/cli/features/job_workflow.py +906 -0
- praisonai_code/cli/features/jobs.py +632 -0
- praisonai_code/cli/features/knowledge.py +531 -0
- praisonai_code/cli/features/knowledge_cli.py +438 -0
- praisonai_code/cli/features/lite.py +244 -0
- praisonai_code/cli/features/logs.py +200 -0
- praisonai_code/cli/features/lsp_cli.py +225 -0
- praisonai_code/cli/features/lsp_diagnostics.py +185 -0
- praisonai_code/cli/features/mcp.py +344 -0
- praisonai_code/cli/features/message_queue.py +587 -0
- praisonai_code/cli/features/metrics.py +210 -0
- praisonai_code/cli/features/migrate.py +1329 -0
- praisonai_code/cli/features/migration_flow.py +463 -0
- praisonai_code/cli/features/migration_spec.py +276 -0
- praisonai_code/cli/features/n8n.py +703 -0
- praisonai_code/cli/features/observability.py +293 -0
- praisonai_code/cli/features/ollama.py +361 -0
- praisonai_code/cli/features/output_modes.py +155 -0
- praisonai_code/cli/features/output_style.py +273 -0
- praisonai_code/cli/features/package.py +631 -0
- praisonai_code/cli/features/performance.py +308 -0
- praisonai_code/cli/features/persistence.py +636 -0
- praisonai_code/cli/features/profiler/__init__.py +81 -0
- praisonai_code/cli/features/profiler/core.py +558 -0
- praisonai_code/cli/features/profiler/optimizations.py +652 -0
- praisonai_code/cli/features/profiler/suite.py +386 -0
- praisonai_code/cli/features/queue/__init__.py +73 -0
- praisonai_code/cli/features/queue/manager.py +435 -0
- praisonai_code/cli/features/queue/models.py +289 -0
- praisonai_code/cli/features/queue/persistence.py +564 -0
- praisonai_code/cli/features/queue/scheduler.py +529 -0
- praisonai_code/cli/features/queue/worker.py +400 -0
- praisonai_code/cli/features/recipe.py +2187 -0
- praisonai_code/cli/features/recipe_creator.py +996 -0
- praisonai_code/cli/features/recipe_optimizer.py +1364 -0
- praisonai_code/cli/features/recipe_prompts.py +226 -0
- praisonai_code/cli/features/registry.py +229 -0
- praisonai_code/cli/features/repo_map.py +860 -0
- praisonai_code/cli/features/router.py +466 -0
- praisonai_code/cli/features/safe_shell.py +427 -0
- praisonai_code/cli/features/sandbox_cli.py +283 -0
- praisonai_code/cli/features/sandbox_executor.py +536 -0
- praisonai_code/cli/features/sdk_knowledge.py +500 -0
- praisonai_code/cli/features/session.py +222 -0
- praisonai_code/cli/features/session_checkpoints.py +208 -0
- praisonai_code/cli/features/setup/__init__.py +9 -0
- praisonai_code/cli/features/setup/handler.py +355 -0
- praisonai_code/cli/features/setup/templates.py +62 -0
- praisonai_code/cli/features/skills.py +940 -0
- praisonai_code/cli/features/slash_commands.py +692 -0
- praisonai_code/cli/features/telemetry.py +179 -0
- praisonai_code/cli/features/templates.py +1390 -0
- praisonai_code/cli/features/thinking.py +343 -0
- praisonai_code/cli/features/todo.py +334 -0
- praisonai_code/cli/features/tools.py +680 -0
- praisonai_code/cli/features/tui/__init__.py +83 -0
- praisonai_code/cli/features/tui/app.py +871 -0
- praisonai_code/cli/features/tui/cli.py +580 -0
- praisonai_code/cli/features/tui/config.py +150 -0
- praisonai_code/cli/features/tui/debug.py +526 -0
- praisonai_code/cli/features/tui/events.py +99 -0
- praisonai_code/cli/features/tui/mock_provider.py +328 -0
- praisonai_code/cli/features/tui/orchestrator.py +652 -0
- praisonai_code/cli/features/tui/screens/__init__.py +50 -0
- praisonai_code/cli/features/tui/screens/help.py +157 -0
- praisonai_code/cli/features/tui/screens/main.py +568 -0
- praisonai_code/cli/features/tui/screens/queue.py +174 -0
- praisonai_code/cli/features/tui/screens/session.py +124 -0
- praisonai_code/cli/features/tui/screens/settings.py +148 -0
- praisonai_code/cli/features/tui/session_store.py +198 -0
- praisonai_code/cli/features/tui/widgets/__init__.py +56 -0
- praisonai_code/cli/features/tui/widgets/chat.py +263 -0
- praisonai_code/cli/features/tui/widgets/command_popup.py +258 -0
- praisonai_code/cli/features/tui/widgets/composer.py +292 -0
- praisonai_code/cli/features/tui/widgets/file_popup.py +207 -0
- praisonai_code/cli/features/tui/widgets/queue_panel.py +223 -0
- praisonai_code/cli/features/tui/widgets/status.py +181 -0
- praisonai_code/cli/features/tui/widgets/tool_panel.py +307 -0
- praisonai_code/cli/features/wizard.py +289 -0
- praisonai_code/cli/features/workflow.py +802 -0
- praisonai_code/cli/features/yaml_utils.py +321 -0
- praisonai_code/cli/interactive/__init__.py +48 -0
- praisonai_code/cli/interactive/async_tui.py +1218 -0
- praisonai_code/cli/interactive/config.py +139 -0
- praisonai_code/cli/interactive/core.py +618 -0
- praisonai_code/cli/interactive/events.py +131 -0
- praisonai_code/cli/interactive/frontends/__init__.py +31 -0
- praisonai_code/cli/interactive/frontends/rich_frontend.py +462 -0
- praisonai_code/cli/interactive/frontends/textual_frontend.py +157 -0
- praisonai_code/cli/interactive/praison_io.py +502 -0
- praisonai_code/cli/interactive/repl.py +297 -0
- praisonai_code/cli/interactive/split_tui.py +456 -0
- praisonai_code/cli/interactive/tui_app.py +457 -0
- praisonai_code/cli/langfuse_client.py +360 -0
- praisonai_code/cli/main.py +7421 -0
- praisonai_code/cli/output/__init__.py +25 -0
- praisonai_code/cli/output/console.py +456 -0
- praisonai_code/cli/output/event_bridge.py +191 -0
- praisonai_code/cli/schedule_cli.py +54 -0
- praisonai_code/cli/schema_provider.py +23 -0
- praisonai_code/cli/session/__init__.py +16 -0
- praisonai_code/cli/session/resume.py +148 -0
- praisonai_code/cli/session/unified.py +548 -0
- praisonai_code/cli/state/__init__.py +31 -0
- praisonai_code/cli/state/identifiers.py +161 -0
- praisonai_code/cli/state/project_sessions.py +383 -0
- praisonai_code/cli/state/sessions.py +390 -0
- praisonai_code/cli/ui/__init__.py +160 -0
- praisonai_code/cli/ui/config.py +46 -0
- praisonai_code/cli/ui/events.py +61 -0
- praisonai_code/cli/ui/mg_backend.py +342 -0
- praisonai_code/cli/ui/plain.py +133 -0
- praisonai_code/cli/ui/rich_backend.py +162 -0
- praisonai_code/cli/unified_schema.py +655 -0
- praisonai_code/cli/utils/env_utils.py +126 -0
- praisonai_code/cli/utils/project.py +131 -0
- praisonai_code/cli_backends/__init__.py +73 -0
- praisonai_code/cli_backends/claude.py +373 -0
- praisonai_code/cli_backends/registry.py +113 -0
- praisonai_code/runtime/__init__.py +36 -0
- praisonai_code/runtime/__main__.py +81 -0
- praisonai_code/runtime/client.py +131 -0
- praisonai_code/runtime/descriptor.py +209 -0
- praisonai_code/runtime/server.py +356 -0
- praisonai_code-0.0.1.dist-info/METADATA +80 -0
- praisonai_code-0.0.1.dist-info/RECORD +309 -0
- praisonai_code-0.0.1.dist-info/WHEEL +5 -0
- 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)
|