PraisonAI 3.0.0__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/__init__.py +54 -0
- praisonai/__main__.py +15 -0
- praisonai/acp/__init__.py +54 -0
- praisonai/acp/config.py +159 -0
- praisonai/acp/server.py +587 -0
- praisonai/acp/session.py +219 -0
- praisonai/adapters/__init__.py +50 -0
- praisonai/adapters/readers.py +395 -0
- praisonai/adapters/rerankers.py +315 -0
- praisonai/adapters/retrievers.py +394 -0
- praisonai/adapters/vector_stores.py +409 -0
- praisonai/agent_scheduler.py +337 -0
- praisonai/agents_generator.py +903 -0
- praisonai/api/call.py +292 -0
- praisonai/auto.py +1197 -0
- praisonai/capabilities/__init__.py +275 -0
- praisonai/capabilities/a2a.py +140 -0
- praisonai/capabilities/assistants.py +283 -0
- praisonai/capabilities/audio.py +320 -0
- praisonai/capabilities/batches.py +469 -0
- praisonai/capabilities/completions.py +336 -0
- praisonai/capabilities/container_files.py +155 -0
- praisonai/capabilities/containers.py +93 -0
- praisonai/capabilities/embeddings.py +158 -0
- praisonai/capabilities/files.py +467 -0
- praisonai/capabilities/fine_tuning.py +293 -0
- praisonai/capabilities/guardrails.py +182 -0
- praisonai/capabilities/images.py +330 -0
- praisonai/capabilities/mcp.py +190 -0
- praisonai/capabilities/messages.py +270 -0
- praisonai/capabilities/moderations.py +154 -0
- praisonai/capabilities/ocr.py +217 -0
- praisonai/capabilities/passthrough.py +204 -0
- praisonai/capabilities/rag.py +207 -0
- praisonai/capabilities/realtime.py +160 -0
- praisonai/capabilities/rerank.py +165 -0
- praisonai/capabilities/responses.py +266 -0
- praisonai/capabilities/search.py +109 -0
- praisonai/capabilities/skills.py +133 -0
- praisonai/capabilities/vector_store_files.py +334 -0
- praisonai/capabilities/vector_stores.py +304 -0
- praisonai/capabilities/videos.py +141 -0
- praisonai/chainlit_ui.py +304 -0
- praisonai/chat/__init__.py +106 -0
- praisonai/chat/app.py +125 -0
- praisonai/cli/__init__.py +26 -0
- praisonai/cli/app.py +213 -0
- praisonai/cli/commands/__init__.py +75 -0
- praisonai/cli/commands/acp.py +70 -0
- praisonai/cli/commands/completion.py +333 -0
- praisonai/cli/commands/config.py +166 -0
- praisonai/cli/commands/debug.py +142 -0
- praisonai/cli/commands/diag.py +55 -0
- praisonai/cli/commands/doctor.py +166 -0
- praisonai/cli/commands/environment.py +179 -0
- praisonai/cli/commands/lsp.py +112 -0
- praisonai/cli/commands/mcp.py +210 -0
- praisonai/cli/commands/profile.py +457 -0
- praisonai/cli/commands/run.py +228 -0
- praisonai/cli/commands/schedule.py +150 -0
- praisonai/cli/commands/serve.py +97 -0
- praisonai/cli/commands/session.py +212 -0
- praisonai/cli/commands/traces.py +145 -0
- praisonai/cli/commands/version.py +101 -0
- praisonai/cli/configuration/__init__.py +18 -0
- praisonai/cli/configuration/loader.py +353 -0
- praisonai/cli/configuration/paths.py +114 -0
- praisonai/cli/configuration/schema.py +164 -0
- praisonai/cli/features/__init__.py +268 -0
- praisonai/cli/features/acp.py +236 -0
- praisonai/cli/features/action_orchestrator.py +546 -0
- praisonai/cli/features/agent_scheduler.py +773 -0
- praisonai/cli/features/agent_tools.py +474 -0
- praisonai/cli/features/agents.py +375 -0
- praisonai/cli/features/at_mentions.py +471 -0
- praisonai/cli/features/auto_memory.py +182 -0
- praisonai/cli/features/autonomy_mode.py +490 -0
- praisonai/cli/features/background.py +356 -0
- praisonai/cli/features/base.py +168 -0
- praisonai/cli/features/capabilities.py +1326 -0
- praisonai/cli/features/checkpoints.py +338 -0
- praisonai/cli/features/code_intelligence.py +652 -0
- praisonai/cli/features/compaction.py +294 -0
- praisonai/cli/features/compare.py +534 -0
- praisonai/cli/features/cost_tracker.py +514 -0
- praisonai/cli/features/debug.py +810 -0
- praisonai/cli/features/deploy.py +517 -0
- praisonai/cli/features/diag.py +289 -0
- praisonai/cli/features/doctor/__init__.py +63 -0
- praisonai/cli/features/doctor/checks/__init__.py +24 -0
- praisonai/cli/features/doctor/checks/acp_checks.py +240 -0
- praisonai/cli/features/doctor/checks/config_checks.py +366 -0
- praisonai/cli/features/doctor/checks/db_checks.py +366 -0
- praisonai/cli/features/doctor/checks/env_checks.py +543 -0
- praisonai/cli/features/doctor/checks/lsp_checks.py +199 -0
- praisonai/cli/features/doctor/checks/mcp_checks.py +349 -0
- praisonai/cli/features/doctor/checks/memory_checks.py +268 -0
- praisonai/cli/features/doctor/checks/network_checks.py +251 -0
- praisonai/cli/features/doctor/checks/obs_checks.py +328 -0
- praisonai/cli/features/doctor/checks/performance_checks.py +235 -0
- praisonai/cli/features/doctor/checks/permissions_checks.py +259 -0
- praisonai/cli/features/doctor/checks/selftest_checks.py +322 -0
- praisonai/cli/features/doctor/checks/serve_checks.py +426 -0
- praisonai/cli/features/doctor/checks/skills_checks.py +231 -0
- praisonai/cli/features/doctor/checks/tools_checks.py +371 -0
- praisonai/cli/features/doctor/engine.py +266 -0
- praisonai/cli/features/doctor/formatters.py +310 -0
- praisonai/cli/features/doctor/handler.py +397 -0
- praisonai/cli/features/doctor/models.py +264 -0
- praisonai/cli/features/doctor/registry.py +239 -0
- praisonai/cli/features/endpoints.py +1019 -0
- praisonai/cli/features/eval.py +560 -0
- praisonai/cli/features/external_agents.py +231 -0
- praisonai/cli/features/fast_context.py +410 -0
- praisonai/cli/features/flow_display.py +566 -0
- praisonai/cli/features/git_integration.py +651 -0
- praisonai/cli/features/guardrail.py +171 -0
- praisonai/cli/features/handoff.py +185 -0
- praisonai/cli/features/hooks.py +583 -0
- praisonai/cli/features/image.py +384 -0
- praisonai/cli/features/interactive_runtime.py +585 -0
- praisonai/cli/features/interactive_tools.py +380 -0
- praisonai/cli/features/interactive_tui.py +603 -0
- praisonai/cli/features/jobs.py +632 -0
- praisonai/cli/features/knowledge.py +531 -0
- praisonai/cli/features/lite.py +244 -0
- praisonai/cli/features/lsp_cli.py +225 -0
- praisonai/cli/features/mcp.py +169 -0
- praisonai/cli/features/message_queue.py +587 -0
- praisonai/cli/features/metrics.py +211 -0
- praisonai/cli/features/n8n.py +673 -0
- praisonai/cli/features/observability.py +293 -0
- praisonai/cli/features/ollama.py +361 -0
- praisonai/cli/features/output_style.py +273 -0
- praisonai/cli/features/package.py +631 -0
- praisonai/cli/features/performance.py +308 -0
- praisonai/cli/features/persistence.py +636 -0
- praisonai/cli/features/profile.py +226 -0
- praisonai/cli/features/profiler/__init__.py +81 -0
- praisonai/cli/features/profiler/core.py +558 -0
- praisonai/cli/features/profiler/optimizations.py +652 -0
- praisonai/cli/features/profiler/suite.py +386 -0
- praisonai/cli/features/profiling.py +350 -0
- praisonai/cli/features/queue/__init__.py +73 -0
- praisonai/cli/features/queue/manager.py +395 -0
- praisonai/cli/features/queue/models.py +286 -0
- praisonai/cli/features/queue/persistence.py +564 -0
- praisonai/cli/features/queue/scheduler.py +484 -0
- praisonai/cli/features/queue/worker.py +372 -0
- praisonai/cli/features/recipe.py +1723 -0
- praisonai/cli/features/recipes.py +449 -0
- praisonai/cli/features/registry.py +229 -0
- praisonai/cli/features/repo_map.py +860 -0
- praisonai/cli/features/router.py +466 -0
- praisonai/cli/features/sandbox_executor.py +515 -0
- praisonai/cli/features/serve.py +829 -0
- praisonai/cli/features/session.py +222 -0
- praisonai/cli/features/skills.py +856 -0
- praisonai/cli/features/slash_commands.py +650 -0
- praisonai/cli/features/telemetry.py +179 -0
- praisonai/cli/features/templates.py +1384 -0
- praisonai/cli/features/thinking.py +305 -0
- praisonai/cli/features/todo.py +334 -0
- praisonai/cli/features/tools.py +680 -0
- praisonai/cli/features/tui/__init__.py +83 -0
- praisonai/cli/features/tui/app.py +580 -0
- praisonai/cli/features/tui/cli.py +566 -0
- praisonai/cli/features/tui/debug.py +511 -0
- praisonai/cli/features/tui/events.py +99 -0
- praisonai/cli/features/tui/mock_provider.py +328 -0
- praisonai/cli/features/tui/orchestrator.py +652 -0
- praisonai/cli/features/tui/screens/__init__.py +50 -0
- praisonai/cli/features/tui/screens/main.py +245 -0
- praisonai/cli/features/tui/screens/queue.py +174 -0
- praisonai/cli/features/tui/screens/session.py +124 -0
- praisonai/cli/features/tui/screens/settings.py +148 -0
- praisonai/cli/features/tui/widgets/__init__.py +56 -0
- praisonai/cli/features/tui/widgets/chat.py +261 -0
- praisonai/cli/features/tui/widgets/composer.py +224 -0
- praisonai/cli/features/tui/widgets/queue_panel.py +200 -0
- praisonai/cli/features/tui/widgets/status.py +167 -0
- praisonai/cli/features/tui/widgets/tool_panel.py +248 -0
- praisonai/cli/features/workflow.py +720 -0
- praisonai/cli/legacy.py +236 -0
- praisonai/cli/main.py +5559 -0
- praisonai/cli/schedule_cli.py +54 -0
- praisonai/cli/state/__init__.py +31 -0
- praisonai/cli/state/identifiers.py +161 -0
- praisonai/cli/state/sessions.py +313 -0
- praisonai/code/__init__.py +93 -0
- praisonai/code/agent_tools.py +344 -0
- praisonai/code/diff/__init__.py +21 -0
- praisonai/code/diff/diff_strategy.py +432 -0
- praisonai/code/tools/__init__.py +27 -0
- praisonai/code/tools/apply_diff.py +221 -0
- praisonai/code/tools/execute_command.py +275 -0
- praisonai/code/tools/list_files.py +274 -0
- praisonai/code/tools/read_file.py +206 -0
- praisonai/code/tools/search_replace.py +248 -0
- praisonai/code/tools/write_file.py +217 -0
- praisonai/code/utils/__init__.py +46 -0
- praisonai/code/utils/file_utils.py +307 -0
- praisonai/code/utils/ignore_utils.py +308 -0
- praisonai/code/utils/text_utils.py +276 -0
- praisonai/db/__init__.py +64 -0
- praisonai/db/adapter.py +531 -0
- praisonai/deploy/__init__.py +62 -0
- praisonai/deploy/api.py +231 -0
- praisonai/deploy/docker.py +454 -0
- praisonai/deploy/doctor.py +367 -0
- praisonai/deploy/main.py +327 -0
- praisonai/deploy/models.py +179 -0
- praisonai/deploy/providers/__init__.py +33 -0
- praisonai/deploy/providers/aws.py +331 -0
- praisonai/deploy/providers/azure.py +358 -0
- praisonai/deploy/providers/base.py +101 -0
- praisonai/deploy/providers/gcp.py +314 -0
- praisonai/deploy/schema.py +208 -0
- praisonai/deploy.py +185 -0
- praisonai/endpoints/__init__.py +53 -0
- praisonai/endpoints/a2u_server.py +410 -0
- praisonai/endpoints/discovery.py +165 -0
- praisonai/endpoints/providers/__init__.py +28 -0
- praisonai/endpoints/providers/a2a.py +253 -0
- praisonai/endpoints/providers/a2u.py +208 -0
- praisonai/endpoints/providers/agents_api.py +171 -0
- praisonai/endpoints/providers/base.py +231 -0
- praisonai/endpoints/providers/mcp.py +263 -0
- praisonai/endpoints/providers/recipe.py +206 -0
- praisonai/endpoints/providers/tools_mcp.py +150 -0
- praisonai/endpoints/registry.py +131 -0
- praisonai/endpoints/server.py +161 -0
- praisonai/inbuilt_tools/__init__.py +24 -0
- praisonai/inbuilt_tools/autogen_tools.py +117 -0
- praisonai/inc/__init__.py +2 -0
- praisonai/inc/config.py +96 -0
- praisonai/inc/models.py +155 -0
- praisonai/integrations/__init__.py +56 -0
- praisonai/integrations/base.py +303 -0
- praisonai/integrations/claude_code.py +270 -0
- praisonai/integrations/codex_cli.py +255 -0
- praisonai/integrations/cursor_cli.py +195 -0
- praisonai/integrations/gemini_cli.py +222 -0
- praisonai/jobs/__init__.py +67 -0
- praisonai/jobs/executor.py +425 -0
- praisonai/jobs/models.py +230 -0
- praisonai/jobs/router.py +314 -0
- praisonai/jobs/server.py +186 -0
- praisonai/jobs/store.py +203 -0
- praisonai/llm/__init__.py +66 -0
- praisonai/llm/registry.py +382 -0
- praisonai/mcp_server/__init__.py +152 -0
- praisonai/mcp_server/adapters/__init__.py +74 -0
- praisonai/mcp_server/adapters/agents.py +128 -0
- praisonai/mcp_server/adapters/capabilities.py +168 -0
- praisonai/mcp_server/adapters/cli_tools.py +568 -0
- praisonai/mcp_server/adapters/extended_capabilities.py +462 -0
- praisonai/mcp_server/adapters/knowledge.py +93 -0
- praisonai/mcp_server/adapters/memory.py +104 -0
- praisonai/mcp_server/adapters/prompts.py +306 -0
- praisonai/mcp_server/adapters/resources.py +124 -0
- praisonai/mcp_server/adapters/tools_bridge.py +280 -0
- praisonai/mcp_server/auth/__init__.py +48 -0
- praisonai/mcp_server/auth/api_key.py +291 -0
- praisonai/mcp_server/auth/oauth.py +460 -0
- praisonai/mcp_server/auth/oidc.py +289 -0
- praisonai/mcp_server/auth/scopes.py +260 -0
- praisonai/mcp_server/cli.py +852 -0
- praisonai/mcp_server/elicitation.py +445 -0
- praisonai/mcp_server/icons.py +302 -0
- praisonai/mcp_server/recipe_adapter.py +573 -0
- praisonai/mcp_server/recipe_cli.py +824 -0
- praisonai/mcp_server/registry.py +703 -0
- praisonai/mcp_server/sampling.py +422 -0
- praisonai/mcp_server/server.py +490 -0
- praisonai/mcp_server/tasks.py +443 -0
- praisonai/mcp_server/transports/__init__.py +18 -0
- praisonai/mcp_server/transports/http_stream.py +376 -0
- praisonai/mcp_server/transports/stdio.py +132 -0
- praisonai/persistence/__init__.py +84 -0
- praisonai/persistence/config.py +238 -0
- praisonai/persistence/conversation/__init__.py +25 -0
- praisonai/persistence/conversation/async_mysql.py +427 -0
- praisonai/persistence/conversation/async_postgres.py +410 -0
- praisonai/persistence/conversation/async_sqlite.py +371 -0
- praisonai/persistence/conversation/base.py +151 -0
- praisonai/persistence/conversation/json_store.py +250 -0
- praisonai/persistence/conversation/mysql.py +387 -0
- praisonai/persistence/conversation/postgres.py +401 -0
- praisonai/persistence/conversation/singlestore.py +240 -0
- praisonai/persistence/conversation/sqlite.py +341 -0
- praisonai/persistence/conversation/supabase.py +203 -0
- praisonai/persistence/conversation/surrealdb.py +287 -0
- praisonai/persistence/factory.py +301 -0
- praisonai/persistence/hooks/__init__.py +18 -0
- praisonai/persistence/hooks/agent_hooks.py +297 -0
- praisonai/persistence/knowledge/__init__.py +26 -0
- praisonai/persistence/knowledge/base.py +144 -0
- praisonai/persistence/knowledge/cassandra.py +232 -0
- praisonai/persistence/knowledge/chroma.py +295 -0
- praisonai/persistence/knowledge/clickhouse.py +242 -0
- praisonai/persistence/knowledge/cosmosdb_vector.py +438 -0
- praisonai/persistence/knowledge/couchbase.py +286 -0
- praisonai/persistence/knowledge/lancedb.py +216 -0
- praisonai/persistence/knowledge/langchain_adapter.py +291 -0
- praisonai/persistence/knowledge/lightrag_adapter.py +212 -0
- praisonai/persistence/knowledge/llamaindex_adapter.py +256 -0
- praisonai/persistence/knowledge/milvus.py +277 -0
- praisonai/persistence/knowledge/mongodb_vector.py +306 -0
- praisonai/persistence/knowledge/pgvector.py +335 -0
- praisonai/persistence/knowledge/pinecone.py +253 -0
- praisonai/persistence/knowledge/qdrant.py +301 -0
- praisonai/persistence/knowledge/redis_vector.py +291 -0
- praisonai/persistence/knowledge/singlestore_vector.py +299 -0
- praisonai/persistence/knowledge/surrealdb_vector.py +309 -0
- praisonai/persistence/knowledge/upstash_vector.py +266 -0
- praisonai/persistence/knowledge/weaviate.py +223 -0
- praisonai/persistence/migrations/__init__.py +10 -0
- praisonai/persistence/migrations/manager.py +251 -0
- praisonai/persistence/orchestrator.py +406 -0
- praisonai/persistence/state/__init__.py +21 -0
- praisonai/persistence/state/async_mongodb.py +200 -0
- praisonai/persistence/state/base.py +107 -0
- praisonai/persistence/state/dynamodb.py +226 -0
- praisonai/persistence/state/firestore.py +175 -0
- praisonai/persistence/state/gcs.py +155 -0
- praisonai/persistence/state/memory.py +245 -0
- praisonai/persistence/state/mongodb.py +158 -0
- praisonai/persistence/state/redis.py +190 -0
- praisonai/persistence/state/upstash.py +144 -0
- praisonai/persistence/tests/__init__.py +3 -0
- praisonai/persistence/tests/test_all_backends.py +633 -0
- praisonai/profiler.py +1214 -0
- praisonai/recipe/__init__.py +134 -0
- praisonai/recipe/bridge.py +278 -0
- praisonai/recipe/core.py +893 -0
- praisonai/recipe/exceptions.py +54 -0
- praisonai/recipe/history.py +402 -0
- praisonai/recipe/models.py +266 -0
- praisonai/recipe/operations.py +440 -0
- praisonai/recipe/policy.py +422 -0
- praisonai/recipe/registry.py +849 -0
- praisonai/recipe/runtime.py +214 -0
- praisonai/recipe/security.py +711 -0
- praisonai/recipe/serve.py +859 -0
- praisonai/recipe/server.py +613 -0
- praisonai/scheduler/__init__.py +45 -0
- praisonai/scheduler/agent_scheduler.py +552 -0
- praisonai/scheduler/base.py +124 -0
- praisonai/scheduler/daemon_manager.py +225 -0
- praisonai/scheduler/state_manager.py +155 -0
- praisonai/scheduler/yaml_loader.py +193 -0
- praisonai/scheduler.py +194 -0
- praisonai/setup/__init__.py +1 -0
- praisonai/setup/build.py +21 -0
- praisonai/setup/post_install.py +23 -0
- praisonai/setup/setup_conda_env.py +25 -0
- praisonai/setup.py +16 -0
- praisonai/templates/__init__.py +116 -0
- praisonai/templates/cache.py +364 -0
- praisonai/templates/dependency_checker.py +358 -0
- praisonai/templates/discovery.py +391 -0
- praisonai/templates/loader.py +564 -0
- praisonai/templates/registry.py +511 -0
- praisonai/templates/resolver.py +206 -0
- praisonai/templates/security.py +327 -0
- praisonai/templates/tool_override.py +498 -0
- praisonai/templates/tools_doctor.py +256 -0
- praisonai/test.py +105 -0
- praisonai/train.py +562 -0
- praisonai/train_vision.py +306 -0
- praisonai/ui/agents.py +824 -0
- praisonai/ui/callbacks.py +57 -0
- praisonai/ui/chainlit_compat.py +246 -0
- praisonai/ui/chat.py +532 -0
- praisonai/ui/code.py +717 -0
- praisonai/ui/colab.py +474 -0
- praisonai/ui/colab_chainlit.py +81 -0
- praisonai/ui/components/aicoder.py +284 -0
- praisonai/ui/context.py +283 -0
- praisonai/ui/database_config.py +56 -0
- praisonai/ui/db.py +294 -0
- praisonai/ui/realtime.py +488 -0
- praisonai/ui/realtimeclient/__init__.py +756 -0
- praisonai/ui/realtimeclient/tools.py +242 -0
- praisonai/ui/sql_alchemy.py +710 -0
- praisonai/upload_vision.py +140 -0
- praisonai/version.py +1 -0
- praisonai-3.0.0.dist-info/METADATA +3493 -0
- praisonai-3.0.0.dist-info/RECORD +393 -0
- praisonai-3.0.0.dist-info/WHEEL +5 -0
- praisonai-3.0.0.dist-info/entry_points.txt +4 -0
- praisonai-3.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,650 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Slash Commands System for PraisonAI CLI.
|
|
3
|
+
|
|
4
|
+
Inspired by Aider's command system and Gemini CLI's slash commands.
|
|
5
|
+
Provides interactive commands like /help, /cost, /plan, /model, /clear, etc.
|
|
6
|
+
|
|
7
|
+
Architecture:
|
|
8
|
+
- SlashCommand: Base class for all commands
|
|
9
|
+
- SlashCommandRegistry: Manages command registration and lookup
|
|
10
|
+
- SlashCommandParser: Parses user input for slash commands
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from dataclasses import dataclass, field
|
|
14
|
+
from typing import Any, Callable, Dict, List, Optional
|
|
15
|
+
from enum import Enum
|
|
16
|
+
import logging
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class CommandKind(Enum):
|
|
22
|
+
"""Type of slash command."""
|
|
23
|
+
BUILT_IN = "built_in"
|
|
24
|
+
CUSTOM = "custom"
|
|
25
|
+
MCP = "mcp"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass
|
|
29
|
+
class SlashCommand:
|
|
30
|
+
"""
|
|
31
|
+
Represents a slash command.
|
|
32
|
+
|
|
33
|
+
Attributes:
|
|
34
|
+
name: Primary command name (e.g., "help", "cost")
|
|
35
|
+
description: Short description for help text
|
|
36
|
+
action: Callable that executes the command
|
|
37
|
+
alt_names: Alternative names/aliases
|
|
38
|
+
sub_commands: Nested sub-commands
|
|
39
|
+
kind: Type of command (built-in, custom, MCP)
|
|
40
|
+
auto_execute: Whether to execute without confirmation
|
|
41
|
+
"""
|
|
42
|
+
name: str
|
|
43
|
+
description: str
|
|
44
|
+
action: Optional[Callable[["CommandContext", str], Any]] = None
|
|
45
|
+
alt_names: List[str] = field(default_factory=list)
|
|
46
|
+
sub_commands: List["SlashCommand"] = field(default_factory=list)
|
|
47
|
+
kind: CommandKind = CommandKind.BUILT_IN
|
|
48
|
+
auto_execute: bool = True
|
|
49
|
+
|
|
50
|
+
def __post_init__(self):
|
|
51
|
+
"""Validate command after initialization."""
|
|
52
|
+
if not self.name:
|
|
53
|
+
raise ValueError("Command name cannot be empty")
|
|
54
|
+
if not self.name.isalnum() and "_" not in self.name and "-" not in self.name:
|
|
55
|
+
raise ValueError(f"Invalid command name: {self.name}")
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@dataclass
|
|
59
|
+
class CommandContext:
|
|
60
|
+
"""
|
|
61
|
+
Context passed to command actions.
|
|
62
|
+
|
|
63
|
+
Contains all services and state needed by commands.
|
|
64
|
+
"""
|
|
65
|
+
# Core services
|
|
66
|
+
agent: Any = None # PraisonAI Agent instance
|
|
67
|
+
config: Dict[str, Any] = field(default_factory=dict)
|
|
68
|
+
|
|
69
|
+
# Session state
|
|
70
|
+
session_id: Optional[str] = None
|
|
71
|
+
session_start_time: Optional[float] = None
|
|
72
|
+
|
|
73
|
+
# Metrics
|
|
74
|
+
total_tokens: int = 0
|
|
75
|
+
total_cost: float = 0.0
|
|
76
|
+
prompt_count: int = 0
|
|
77
|
+
|
|
78
|
+
# UI callbacks
|
|
79
|
+
print_fn: Callable[[str], None] = print
|
|
80
|
+
input_fn: Callable[[str], str] = input
|
|
81
|
+
|
|
82
|
+
# Command invocation info
|
|
83
|
+
raw_input: str = ""
|
|
84
|
+
command_name: str = ""
|
|
85
|
+
args: str = ""
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
@dataclass
|
|
89
|
+
class ParsedSlashCommand:
|
|
90
|
+
"""Result of parsing a slash command."""
|
|
91
|
+
command: Optional[SlashCommand] = None
|
|
92
|
+
args: str = ""
|
|
93
|
+
canonical_path: List[str] = field(default_factory=list)
|
|
94
|
+
error: Optional[str] = None
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class SlashCommandRegistry:
|
|
98
|
+
"""
|
|
99
|
+
Registry for slash commands.
|
|
100
|
+
|
|
101
|
+
Manages command registration, lookup, and help generation.
|
|
102
|
+
"""
|
|
103
|
+
|
|
104
|
+
def __init__(self):
|
|
105
|
+
self._commands: Dict[str, SlashCommand] = {}
|
|
106
|
+
self._aliases: Dict[str, str] = {} # alias -> primary name
|
|
107
|
+
|
|
108
|
+
def register(self, command: SlashCommand) -> None:
|
|
109
|
+
"""Register a slash command."""
|
|
110
|
+
if command.name in self._commands:
|
|
111
|
+
logger.warning(f"Overwriting existing command: {command.name}")
|
|
112
|
+
|
|
113
|
+
self._commands[command.name] = command
|
|
114
|
+
|
|
115
|
+
# Register aliases
|
|
116
|
+
for alias in command.alt_names:
|
|
117
|
+
if alias in self._aliases:
|
|
118
|
+
logger.warning(f"Alias '{alias}' already registered")
|
|
119
|
+
self._aliases[alias] = command.name
|
|
120
|
+
|
|
121
|
+
def unregister(self, name: str) -> bool:
|
|
122
|
+
"""Unregister a command by name."""
|
|
123
|
+
if name in self._commands:
|
|
124
|
+
cmd = self._commands.pop(name)
|
|
125
|
+
# Remove aliases
|
|
126
|
+
for alias in cmd.alt_names:
|
|
127
|
+
self._aliases.pop(alias, None)
|
|
128
|
+
return True
|
|
129
|
+
return False
|
|
130
|
+
|
|
131
|
+
def get(self, name: str) -> Optional[SlashCommand]:
|
|
132
|
+
"""Get a command by name or alias."""
|
|
133
|
+
# Check primary name first
|
|
134
|
+
if name in self._commands:
|
|
135
|
+
return self._commands[name]
|
|
136
|
+
# Check aliases
|
|
137
|
+
if name in self._aliases:
|
|
138
|
+
return self._commands.get(self._aliases[name])
|
|
139
|
+
return None
|
|
140
|
+
|
|
141
|
+
def get_all(self) -> List[SlashCommand]:
|
|
142
|
+
"""Get all registered commands."""
|
|
143
|
+
return list(self._commands.values())
|
|
144
|
+
|
|
145
|
+
def get_names(self) -> List[str]:
|
|
146
|
+
"""Get all command names (including aliases)."""
|
|
147
|
+
names = list(self._commands.keys())
|
|
148
|
+
names.extend(self._aliases.keys())
|
|
149
|
+
return sorted(set(names))
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
class SlashCommandParser:
|
|
153
|
+
"""
|
|
154
|
+
Parser for slash commands.
|
|
155
|
+
|
|
156
|
+
Handles parsing of user input into command and arguments.
|
|
157
|
+
"""
|
|
158
|
+
|
|
159
|
+
def __init__(self, registry: SlashCommandRegistry):
|
|
160
|
+
self.registry = registry
|
|
161
|
+
|
|
162
|
+
def is_slash_command(self, query: str) -> bool:
|
|
163
|
+
"""Check if input is a slash command."""
|
|
164
|
+
query = query.strip()
|
|
165
|
+
if not query.startswith('/'):
|
|
166
|
+
return False
|
|
167
|
+
# Exclude code comments
|
|
168
|
+
if query.startswith('//') or query.startswith('/*'):
|
|
169
|
+
return False
|
|
170
|
+
return True
|
|
171
|
+
|
|
172
|
+
def parse(self, query: str) -> ParsedSlashCommand:
|
|
173
|
+
"""
|
|
174
|
+
Parse a slash command string.
|
|
175
|
+
|
|
176
|
+
Args:
|
|
177
|
+
query: Raw input string (e.g., "/help model" or "/cost")
|
|
178
|
+
|
|
179
|
+
Returns:
|
|
180
|
+
ParsedSlashCommand with resolved command and arguments
|
|
181
|
+
"""
|
|
182
|
+
if not self.is_slash_command(query):
|
|
183
|
+
return ParsedSlashCommand(error="Not a slash command")
|
|
184
|
+
|
|
185
|
+
trimmed = query.strip()
|
|
186
|
+
parts = trimmed[1:].strip().split() # Remove leading /
|
|
187
|
+
|
|
188
|
+
if not parts:
|
|
189
|
+
return ParsedSlashCommand(error="Empty command")
|
|
190
|
+
|
|
191
|
+
command_path = parts
|
|
192
|
+
canonical_path: List[str] = []
|
|
193
|
+
current_commands = self.registry.get_all()
|
|
194
|
+
command_to_execute: Optional[SlashCommand] = None
|
|
195
|
+
path_index = 0
|
|
196
|
+
|
|
197
|
+
for part in command_path:
|
|
198
|
+
# First pass: exact match on primary name
|
|
199
|
+
found_command = None
|
|
200
|
+
for cmd in current_commands:
|
|
201
|
+
if cmd.name == part:
|
|
202
|
+
found_command = cmd
|
|
203
|
+
break
|
|
204
|
+
|
|
205
|
+
# Second pass: check aliases
|
|
206
|
+
if not found_command:
|
|
207
|
+
for cmd in current_commands:
|
|
208
|
+
if part in cmd.alt_names:
|
|
209
|
+
found_command = cmd
|
|
210
|
+
break
|
|
211
|
+
|
|
212
|
+
if found_command:
|
|
213
|
+
command_to_execute = found_command
|
|
214
|
+
canonical_path.append(found_command.name)
|
|
215
|
+
path_index += 1
|
|
216
|
+
if found_command.sub_commands:
|
|
217
|
+
current_commands = found_command.sub_commands
|
|
218
|
+
else:
|
|
219
|
+
break
|
|
220
|
+
else:
|
|
221
|
+
break
|
|
222
|
+
|
|
223
|
+
args = ' '.join(parts[path_index:])
|
|
224
|
+
|
|
225
|
+
return ParsedSlashCommand(
|
|
226
|
+
command=command_to_execute,
|
|
227
|
+
args=args,
|
|
228
|
+
canonical_path=canonical_path
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
# ============================================================================
|
|
233
|
+
# Built-in Commands
|
|
234
|
+
# ============================================================================
|
|
235
|
+
|
|
236
|
+
def cmd_help(context: CommandContext, args: str) -> Dict[str, Any]:
|
|
237
|
+
"""Show help for commands."""
|
|
238
|
+
from rich.console import Console
|
|
239
|
+
from rich.table import Table
|
|
240
|
+
|
|
241
|
+
console = Console()
|
|
242
|
+
|
|
243
|
+
if args:
|
|
244
|
+
# Help for specific command
|
|
245
|
+
registry = context.config.get("command_registry")
|
|
246
|
+
if registry:
|
|
247
|
+
cmd = registry.get(args)
|
|
248
|
+
if cmd:
|
|
249
|
+
console.print(f"\n[bold cyan]/{cmd.name}[/bold cyan]")
|
|
250
|
+
console.print(f" {cmd.description}")
|
|
251
|
+
if cmd.alt_names:
|
|
252
|
+
console.print(f" Aliases: {', '.join(cmd.alt_names)}")
|
|
253
|
+
if cmd.sub_commands:
|
|
254
|
+
console.print("\n Sub-commands:")
|
|
255
|
+
for sub in cmd.sub_commands:
|
|
256
|
+
console.print(f" /{cmd.name} {sub.name} - {sub.description}")
|
|
257
|
+
return {"type": "help", "command": cmd.name}
|
|
258
|
+
else:
|
|
259
|
+
console.print(f"[red]Unknown command: {args}[/red]")
|
|
260
|
+
return {"type": "error", "message": f"Unknown command: {args}"}
|
|
261
|
+
|
|
262
|
+
# General help
|
|
263
|
+
table = Table(title="Available Commands", show_header=True)
|
|
264
|
+
table.add_column("Command", style="cyan")
|
|
265
|
+
table.add_column("Description")
|
|
266
|
+
|
|
267
|
+
registry = context.config.get("command_registry")
|
|
268
|
+
if registry:
|
|
269
|
+
for cmd in registry.get_all():
|
|
270
|
+
aliases = f" ({', '.join(cmd.alt_names)})" if cmd.alt_names else ""
|
|
271
|
+
table.add_row(f"/{cmd.name}{aliases}", cmd.description)
|
|
272
|
+
|
|
273
|
+
console.print(table)
|
|
274
|
+
return {"type": "help"}
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def cmd_cost(context: CommandContext, args: str) -> Dict[str, Any]:
|
|
278
|
+
"""Show session cost and token usage."""
|
|
279
|
+
from rich.console import Console
|
|
280
|
+
from rich.panel import Panel
|
|
281
|
+
|
|
282
|
+
console = Console()
|
|
283
|
+
|
|
284
|
+
cost_info = f"""
|
|
285
|
+
[bold]Session Statistics[/bold]
|
|
286
|
+
|
|
287
|
+
Total Tokens: {context.total_tokens:,}
|
|
288
|
+
Total Cost: ${context.total_cost:.4f}
|
|
289
|
+
Prompts: {context.prompt_count}
|
|
290
|
+
"""
|
|
291
|
+
|
|
292
|
+
if context.session_start_time:
|
|
293
|
+
import time
|
|
294
|
+
duration = time.time() - context.session_start_time
|
|
295
|
+
minutes = int(duration // 60)
|
|
296
|
+
seconds = int(duration % 60)
|
|
297
|
+
cost_info += f"Duration: {minutes}m {seconds}s\n"
|
|
298
|
+
|
|
299
|
+
console.print(Panel(cost_info, title="💰 Cost Tracker", border_style="green"))
|
|
300
|
+
|
|
301
|
+
return {
|
|
302
|
+
"type": "stats",
|
|
303
|
+
"tokens": context.total_tokens,
|
|
304
|
+
"cost": context.total_cost,
|
|
305
|
+
"prompts": context.prompt_count
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def cmd_clear(context: CommandContext, args: str) -> Dict[str, Any]:
|
|
310
|
+
"""Clear the conversation history."""
|
|
311
|
+
from rich.console import Console
|
|
312
|
+
console = Console()
|
|
313
|
+
|
|
314
|
+
# Reset agent conversation if available
|
|
315
|
+
if context.agent and hasattr(context.agent, 'clear_history'):
|
|
316
|
+
context.agent.clear_history()
|
|
317
|
+
|
|
318
|
+
console.print("[green]✓ Conversation cleared[/green]")
|
|
319
|
+
return {"type": "clear"}
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def cmd_model(context: CommandContext, args: str) -> Dict[str, Any]:
|
|
323
|
+
"""Show or change the current model."""
|
|
324
|
+
from rich.console import Console
|
|
325
|
+
console = Console()
|
|
326
|
+
|
|
327
|
+
if args:
|
|
328
|
+
# Change model
|
|
329
|
+
if context.agent and hasattr(context.agent, 'set_model'):
|
|
330
|
+
context.agent.set_model(args)
|
|
331
|
+
console.print(f"[green]✓ Model changed to: {args}[/green]")
|
|
332
|
+
return {"type": "model_change", "model": args}
|
|
333
|
+
else:
|
|
334
|
+
console.print(f"[yellow]Model change requested: {args}[/yellow]")
|
|
335
|
+
return {"type": "model_change", "model": args}
|
|
336
|
+
else:
|
|
337
|
+
# Show current model
|
|
338
|
+
current_model = "unknown"
|
|
339
|
+
if context.agent and hasattr(context.agent, 'llm'):
|
|
340
|
+
current_model = getattr(context.agent.llm, 'model', 'unknown')
|
|
341
|
+
console.print(f"[cyan]Current model: {current_model}[/cyan]")
|
|
342
|
+
return {"type": "model_info", "model": current_model}
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
def cmd_tokens(context: CommandContext, args: str) -> Dict[str, Any]:
|
|
346
|
+
"""Show token usage breakdown."""
|
|
347
|
+
from rich.console import Console
|
|
348
|
+
from rich.table import Table
|
|
349
|
+
|
|
350
|
+
console = Console()
|
|
351
|
+
|
|
352
|
+
table = Table(title="Token Usage", show_header=True)
|
|
353
|
+
table.add_column("Metric", style="cyan")
|
|
354
|
+
table.add_column("Value", justify="right")
|
|
355
|
+
|
|
356
|
+
table.add_row("Total Tokens", f"{context.total_tokens:,}")
|
|
357
|
+
table.add_row("Prompts Sent", str(context.prompt_count))
|
|
358
|
+
if context.prompt_count > 0:
|
|
359
|
+
avg = context.total_tokens / context.prompt_count
|
|
360
|
+
table.add_row("Avg Tokens/Prompt", f"{avg:.0f}")
|
|
361
|
+
|
|
362
|
+
console.print(table)
|
|
363
|
+
return {"type": "tokens", "total": context.total_tokens}
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
def cmd_plan(context: CommandContext, args: str) -> Dict[str, Any]:
|
|
367
|
+
"""Show or create an execution plan."""
|
|
368
|
+
from rich.console import Console
|
|
369
|
+
console = Console()
|
|
370
|
+
|
|
371
|
+
if args:
|
|
372
|
+
console.print(f"[cyan]Creating plan for: {args}[/cyan]")
|
|
373
|
+
# Trigger planning mode
|
|
374
|
+
return {"type": "submit_prompt", "content": f"Create a detailed plan for: {args}"}
|
|
375
|
+
else:
|
|
376
|
+
console.print("[yellow]Usage: /plan <task description>[/yellow]")
|
|
377
|
+
return {"type": "help", "command": "plan"}
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
def cmd_undo(context: CommandContext, args: str) -> Dict[str, Any]:
|
|
381
|
+
"""Undo the last change."""
|
|
382
|
+
from rich.console import Console
|
|
383
|
+
console = Console()
|
|
384
|
+
|
|
385
|
+
# Check for git integration
|
|
386
|
+
try:
|
|
387
|
+
import subprocess
|
|
388
|
+
result = subprocess.run(
|
|
389
|
+
["git", "diff", "--stat", "HEAD~1"],
|
|
390
|
+
capture_output=True,
|
|
391
|
+
text=True,
|
|
392
|
+
cwd="."
|
|
393
|
+
)
|
|
394
|
+
if result.returncode == 0:
|
|
395
|
+
console.print("[yellow]Last commit changes:[/yellow]")
|
|
396
|
+
console.print(result.stdout)
|
|
397
|
+
console.print("\n[cyan]Run 'git reset --soft HEAD~1' to undo[/cyan]")
|
|
398
|
+
else:
|
|
399
|
+
console.print("[red]No git history available[/red]")
|
|
400
|
+
except Exception as e:
|
|
401
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
402
|
+
|
|
403
|
+
return {"type": "undo"}
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
def cmd_diff(context: CommandContext, args: str) -> Dict[str, Any]:
|
|
407
|
+
"""Show git diff of changes."""
|
|
408
|
+
from rich.console import Console
|
|
409
|
+
from rich.syntax import Syntax
|
|
410
|
+
|
|
411
|
+
console = Console()
|
|
412
|
+
|
|
413
|
+
try:
|
|
414
|
+
import subprocess
|
|
415
|
+
result = subprocess.run(
|
|
416
|
+
["git", "diff"],
|
|
417
|
+
capture_output=True,
|
|
418
|
+
text=True,
|
|
419
|
+
cwd="."
|
|
420
|
+
)
|
|
421
|
+
if result.stdout:
|
|
422
|
+
syntax = Syntax(result.stdout, "diff", theme="monokai")
|
|
423
|
+
console.print(syntax)
|
|
424
|
+
else:
|
|
425
|
+
console.print("[green]No uncommitted changes[/green]")
|
|
426
|
+
except Exception as e:
|
|
427
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
428
|
+
|
|
429
|
+
return {"type": "diff"}
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
def cmd_commit(context: CommandContext, args: str) -> Dict[str, Any]:
|
|
433
|
+
"""Commit changes with AI-generated message."""
|
|
434
|
+
from rich.console import Console
|
|
435
|
+
console = Console()
|
|
436
|
+
|
|
437
|
+
console.print("[cyan]Generating commit message...[/cyan]")
|
|
438
|
+
|
|
439
|
+
# This will trigger the commit command handler
|
|
440
|
+
return {"type": "commit", "message": args if args else None}
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
def cmd_exit(context: CommandContext, args: str) -> Dict[str, Any]:
|
|
444
|
+
"""Exit the CLI."""
|
|
445
|
+
from rich.console import Console
|
|
446
|
+
console = Console()
|
|
447
|
+
|
|
448
|
+
console.print("[yellow]Goodbye![/yellow]")
|
|
449
|
+
return {"type": "exit"}
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
def cmd_settings(context: CommandContext, args: str) -> Dict[str, Any]:
|
|
453
|
+
"""Show current settings."""
|
|
454
|
+
from rich.console import Console
|
|
455
|
+
from rich.table import Table
|
|
456
|
+
|
|
457
|
+
console = Console()
|
|
458
|
+
|
|
459
|
+
table = Table(title="Current Settings", show_header=True)
|
|
460
|
+
table.add_column("Setting", style="cyan")
|
|
461
|
+
table.add_column("Value")
|
|
462
|
+
|
|
463
|
+
for key, value in context.config.items():
|
|
464
|
+
if key != "command_registry": # Skip internal objects
|
|
465
|
+
table.add_row(key, str(value))
|
|
466
|
+
|
|
467
|
+
console.print(table)
|
|
468
|
+
return {"type": "settings"}
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
def cmd_map(context: CommandContext, args: str) -> Dict[str, Any]:
|
|
472
|
+
"""Show repository map."""
|
|
473
|
+
from rich.console import Console
|
|
474
|
+
console = Console()
|
|
475
|
+
|
|
476
|
+
console.print("[cyan]Generating repository map...[/cyan]")
|
|
477
|
+
# This will be implemented with RepoMap feature
|
|
478
|
+
return {"type": "map"}
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
# ============================================================================
|
|
482
|
+
# Registry Setup
|
|
483
|
+
# ============================================================================
|
|
484
|
+
|
|
485
|
+
def create_default_registry() -> SlashCommandRegistry:
|
|
486
|
+
"""Create registry with default built-in commands."""
|
|
487
|
+
registry = SlashCommandRegistry()
|
|
488
|
+
|
|
489
|
+
# Core commands
|
|
490
|
+
registry.register(SlashCommand(
|
|
491
|
+
name="help",
|
|
492
|
+
description="Show help for commands",
|
|
493
|
+
action=cmd_help,
|
|
494
|
+
alt_names=["h", "?"]
|
|
495
|
+
))
|
|
496
|
+
|
|
497
|
+
registry.register(SlashCommand(
|
|
498
|
+
name="cost",
|
|
499
|
+
description="Show session cost and token usage",
|
|
500
|
+
action=cmd_cost,
|
|
501
|
+
alt_names=["usage", "stats"]
|
|
502
|
+
))
|
|
503
|
+
|
|
504
|
+
registry.register(SlashCommand(
|
|
505
|
+
name="clear",
|
|
506
|
+
description="Clear conversation history",
|
|
507
|
+
action=cmd_clear,
|
|
508
|
+
alt_names=["reset"]
|
|
509
|
+
))
|
|
510
|
+
|
|
511
|
+
registry.register(SlashCommand(
|
|
512
|
+
name="model",
|
|
513
|
+
description="Show or change the current model",
|
|
514
|
+
action=cmd_model,
|
|
515
|
+
alt_names=["m"]
|
|
516
|
+
))
|
|
517
|
+
|
|
518
|
+
registry.register(SlashCommand(
|
|
519
|
+
name="tokens",
|
|
520
|
+
description="Show token usage breakdown",
|
|
521
|
+
action=cmd_tokens
|
|
522
|
+
))
|
|
523
|
+
|
|
524
|
+
registry.register(SlashCommand(
|
|
525
|
+
name="plan",
|
|
526
|
+
description="Create an execution plan for a task",
|
|
527
|
+
action=cmd_plan
|
|
528
|
+
))
|
|
529
|
+
|
|
530
|
+
registry.register(SlashCommand(
|
|
531
|
+
name="undo",
|
|
532
|
+
description="Undo the last change",
|
|
533
|
+
action=cmd_undo
|
|
534
|
+
))
|
|
535
|
+
|
|
536
|
+
registry.register(SlashCommand(
|
|
537
|
+
name="diff",
|
|
538
|
+
description="Show git diff of changes",
|
|
539
|
+
action=cmd_diff
|
|
540
|
+
))
|
|
541
|
+
|
|
542
|
+
registry.register(SlashCommand(
|
|
543
|
+
name="commit",
|
|
544
|
+
description="Commit changes with AI-generated message",
|
|
545
|
+
action=cmd_commit
|
|
546
|
+
))
|
|
547
|
+
|
|
548
|
+
registry.register(SlashCommand(
|
|
549
|
+
name="exit",
|
|
550
|
+
description="Exit the CLI",
|
|
551
|
+
action=cmd_exit,
|
|
552
|
+
alt_names=["quit", "q"]
|
|
553
|
+
))
|
|
554
|
+
|
|
555
|
+
registry.register(SlashCommand(
|
|
556
|
+
name="settings",
|
|
557
|
+
description="Show current settings",
|
|
558
|
+
action=cmd_settings
|
|
559
|
+
))
|
|
560
|
+
|
|
561
|
+
registry.register(SlashCommand(
|
|
562
|
+
name="map",
|
|
563
|
+
description="Show repository map",
|
|
564
|
+
action=cmd_map,
|
|
565
|
+
alt_names=["repo"]
|
|
566
|
+
))
|
|
567
|
+
|
|
568
|
+
return registry
|
|
569
|
+
|
|
570
|
+
|
|
571
|
+
# ============================================================================
|
|
572
|
+
# Handler for CLI Integration
|
|
573
|
+
# ============================================================================
|
|
574
|
+
|
|
575
|
+
class SlashCommandHandler:
|
|
576
|
+
"""
|
|
577
|
+
Handler for integrating slash commands with PraisonAI CLI.
|
|
578
|
+
"""
|
|
579
|
+
|
|
580
|
+
def __init__(self, verbose: bool = False):
|
|
581
|
+
self.verbose = verbose
|
|
582
|
+
self.registry = create_default_registry()
|
|
583
|
+
self.parser = SlashCommandParser(self.registry)
|
|
584
|
+
self._context: Optional[CommandContext] = None
|
|
585
|
+
|
|
586
|
+
def set_context(self, context: CommandContext) -> None:
|
|
587
|
+
"""Set the command context."""
|
|
588
|
+
self._context = context
|
|
589
|
+
context.config["command_registry"] = self.registry
|
|
590
|
+
|
|
591
|
+
def is_command(self, query: str) -> bool:
|
|
592
|
+
"""Check if input is a slash command."""
|
|
593
|
+
return self.parser.is_slash_command(query)
|
|
594
|
+
|
|
595
|
+
def execute(self, query: str) -> Optional[Dict[str, Any]]:
|
|
596
|
+
"""
|
|
597
|
+
Execute a slash command.
|
|
598
|
+
|
|
599
|
+
Returns:
|
|
600
|
+
Command result dict or None if not a command
|
|
601
|
+
"""
|
|
602
|
+
if not self.is_command(query):
|
|
603
|
+
return None
|
|
604
|
+
|
|
605
|
+
parsed = self.parser.parse(query)
|
|
606
|
+
|
|
607
|
+
if parsed.error:
|
|
608
|
+
if self.verbose:
|
|
609
|
+
print(f"[red]Error: {parsed.error}[/red]")
|
|
610
|
+
return {"type": "error", "message": parsed.error}
|
|
611
|
+
|
|
612
|
+
if not parsed.command:
|
|
613
|
+
return {"type": "error", "message": "Unknown command"}
|
|
614
|
+
|
|
615
|
+
if not parsed.command.action:
|
|
616
|
+
return {"type": "error", "message": f"Command '{parsed.command.name}' has no action"}
|
|
617
|
+
|
|
618
|
+
# Create context if not set
|
|
619
|
+
if not self._context:
|
|
620
|
+
self._context = CommandContext()
|
|
621
|
+
self._context.config["command_registry"] = self.registry
|
|
622
|
+
|
|
623
|
+
self._context.raw_input = query
|
|
624
|
+
self._context.command_name = parsed.command.name
|
|
625
|
+
self._context.args = parsed.args
|
|
626
|
+
|
|
627
|
+
try:
|
|
628
|
+
result = parsed.command.action(self._context, parsed.args)
|
|
629
|
+
return result
|
|
630
|
+
except Exception as e:
|
|
631
|
+
logger.error(f"Error executing command: {e}")
|
|
632
|
+
return {"type": "error", "message": str(e)}
|
|
633
|
+
|
|
634
|
+
def register_command(self, command: SlashCommand) -> None:
|
|
635
|
+
"""Register a custom command."""
|
|
636
|
+
self.registry.register(command)
|
|
637
|
+
|
|
638
|
+
def get_completions(self, partial: str) -> List[str]:
|
|
639
|
+
"""Get command completions for partial input."""
|
|
640
|
+
if not partial.startswith('/'):
|
|
641
|
+
return []
|
|
642
|
+
|
|
643
|
+
partial_cmd = partial[1:].lower()
|
|
644
|
+
completions = []
|
|
645
|
+
|
|
646
|
+
for name in self.registry.get_names():
|
|
647
|
+
if name.lower().startswith(partial_cmd):
|
|
648
|
+
completions.append(f"/{name}")
|
|
649
|
+
|
|
650
|
+
return sorted(completions)
|