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,303 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base class for external CLI tool integrations.
|
|
3
|
+
|
|
4
|
+
Provides a consistent interface for integrating with AI coding CLI tools
|
|
5
|
+
like Claude Code, Gemini CLI, Codex CLI, and Cursor CLI.
|
|
6
|
+
|
|
7
|
+
Features:
|
|
8
|
+
- Lazy availability checking with caching
|
|
9
|
+
- Async subprocess execution
|
|
10
|
+
- Tool wrapper for agent integration
|
|
11
|
+
- Timeout handling
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from abc import ABC, abstractmethod
|
|
15
|
+
from typing import AsyncIterator, Optional, Dict, Any, Tuple, List
|
|
16
|
+
import asyncio
|
|
17
|
+
import shutil
|
|
18
|
+
import os
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class BaseCLIIntegration(ABC):
|
|
22
|
+
"""
|
|
23
|
+
Abstract base class for external CLI tool integrations.
|
|
24
|
+
|
|
25
|
+
Provides:
|
|
26
|
+
- Lazy availability checking with class-level caching
|
|
27
|
+
- Async subprocess execution with timeout handling
|
|
28
|
+
- Tool wrapper for PraisonAI agent integration
|
|
29
|
+
|
|
30
|
+
Subclasses must implement:
|
|
31
|
+
- cli_command property: The CLI command name
|
|
32
|
+
- execute method: Execute the CLI and return result
|
|
33
|
+
- stream method: Stream output from the CLI
|
|
34
|
+
|
|
35
|
+
Example:
|
|
36
|
+
class MyIntegration(BaseCLIIntegration):
|
|
37
|
+
@property
|
|
38
|
+
def cli_command(self) -> str:
|
|
39
|
+
return "my-cli"
|
|
40
|
+
|
|
41
|
+
async def execute(self, prompt: str, **options) -> str:
|
|
42
|
+
return await self.execute_async(["my-cli", "-p", prompt])
|
|
43
|
+
|
|
44
|
+
async def stream(self, prompt: str, **options):
|
|
45
|
+
async for line in self.stream_async(["my-cli", "-p", prompt]):
|
|
46
|
+
yield line
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
# Class-level cache for availability checks (shared across instances)
|
|
50
|
+
_availability_cache: Dict[str, bool] = {}
|
|
51
|
+
|
|
52
|
+
def __init__(self, workspace: str = ".", timeout: int = 300):
|
|
53
|
+
"""
|
|
54
|
+
Initialize the CLI integration.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
workspace: Working directory for CLI execution (default: current dir)
|
|
58
|
+
timeout: Default timeout in seconds for CLI execution (default: 300)
|
|
59
|
+
"""
|
|
60
|
+
self.workspace = workspace
|
|
61
|
+
self.timeout = timeout
|
|
62
|
+
|
|
63
|
+
@property
|
|
64
|
+
@abstractmethod
|
|
65
|
+
def cli_command(self) -> str:
|
|
66
|
+
"""
|
|
67
|
+
Return the CLI command name.
|
|
68
|
+
|
|
69
|
+
This is used for availability checking and tool naming.
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
str: The CLI command (e.g., "claude", "gemini", "codex", "cursor-agent")
|
|
73
|
+
"""
|
|
74
|
+
pass
|
|
75
|
+
|
|
76
|
+
@property
|
|
77
|
+
def is_available(self) -> bool:
|
|
78
|
+
"""
|
|
79
|
+
Check if the CLI tool is installed and available.
|
|
80
|
+
|
|
81
|
+
Uses class-level caching to avoid repeated filesystem checks.
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
bool: True if the CLI is available, False otherwise
|
|
85
|
+
"""
|
|
86
|
+
if self.cli_command not in self._availability_cache:
|
|
87
|
+
self._availability_cache[self.cli_command] = shutil.which(self.cli_command) is not None
|
|
88
|
+
return self._availability_cache[self.cli_command]
|
|
89
|
+
|
|
90
|
+
@abstractmethod
|
|
91
|
+
async def execute(self, prompt: str, **options) -> str:
|
|
92
|
+
"""
|
|
93
|
+
Execute the CLI tool and return the result.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
prompt: The prompt/query to send to the CLI
|
|
97
|
+
**options: Additional options for the CLI
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
str: The CLI output
|
|
101
|
+
"""
|
|
102
|
+
pass
|
|
103
|
+
|
|
104
|
+
@abstractmethod
|
|
105
|
+
async def stream(self, prompt: str, **options) -> AsyncIterator[Dict[str, Any]]:
|
|
106
|
+
"""
|
|
107
|
+
Stream output from the CLI tool.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
prompt: The prompt/query to send to the CLI
|
|
111
|
+
**options: Additional options for the CLI
|
|
112
|
+
|
|
113
|
+
Yields:
|
|
114
|
+
dict: Parsed output events from the CLI
|
|
115
|
+
"""
|
|
116
|
+
pass
|
|
117
|
+
|
|
118
|
+
async def execute_async(self, cmd: List[str], timeout: Optional[int] = None) -> str:
|
|
119
|
+
"""
|
|
120
|
+
Execute a command asynchronously and return stdout.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
cmd: Command and arguments as a list
|
|
124
|
+
timeout: Timeout in seconds (uses self.timeout if not specified)
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
str: The command's stdout
|
|
128
|
+
|
|
129
|
+
Raises:
|
|
130
|
+
TimeoutError: If the command times out
|
|
131
|
+
"""
|
|
132
|
+
timeout = timeout or self.timeout
|
|
133
|
+
|
|
134
|
+
proc = await asyncio.create_subprocess_exec(
|
|
135
|
+
*cmd,
|
|
136
|
+
stdout=asyncio.subprocess.PIPE,
|
|
137
|
+
stderr=asyncio.subprocess.PIPE,
|
|
138
|
+
cwd=self.workspace,
|
|
139
|
+
env=self.get_env()
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
try:
|
|
143
|
+
stdout, stderr = await asyncio.wait_for(
|
|
144
|
+
proc.communicate(),
|
|
145
|
+
timeout=timeout
|
|
146
|
+
)
|
|
147
|
+
return stdout.decode()
|
|
148
|
+
except asyncio.TimeoutError:
|
|
149
|
+
proc.kill()
|
|
150
|
+
await proc.wait()
|
|
151
|
+
raise TimeoutError(f"Command timed out after {timeout}s: {' '.join(cmd)}")
|
|
152
|
+
|
|
153
|
+
async def execute_async_with_stderr(
|
|
154
|
+
self,
|
|
155
|
+
cmd: List[str],
|
|
156
|
+
timeout: Optional[int] = None
|
|
157
|
+
) -> Tuple[str, str]:
|
|
158
|
+
"""
|
|
159
|
+
Execute a command asynchronously and return both stdout and stderr.
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
cmd: Command and arguments as a list
|
|
163
|
+
timeout: Timeout in seconds (uses self.timeout if not specified)
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
Tuple[str, str]: (stdout, stderr)
|
|
167
|
+
|
|
168
|
+
Raises:
|
|
169
|
+
TimeoutError: If the command times out
|
|
170
|
+
"""
|
|
171
|
+
timeout = timeout or self.timeout
|
|
172
|
+
|
|
173
|
+
proc = await asyncio.create_subprocess_exec(
|
|
174
|
+
*cmd,
|
|
175
|
+
stdout=asyncio.subprocess.PIPE,
|
|
176
|
+
stderr=asyncio.subprocess.PIPE,
|
|
177
|
+
cwd=self.workspace,
|
|
178
|
+
env=self.get_env()
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
try:
|
|
182
|
+
stdout, stderr = await asyncio.wait_for(
|
|
183
|
+
proc.communicate(),
|
|
184
|
+
timeout=timeout
|
|
185
|
+
)
|
|
186
|
+
return stdout.decode(), stderr.decode()
|
|
187
|
+
except asyncio.TimeoutError:
|
|
188
|
+
proc.kill()
|
|
189
|
+
await proc.wait()
|
|
190
|
+
raise TimeoutError(f"Command timed out after {timeout}s: {' '.join(cmd)}")
|
|
191
|
+
|
|
192
|
+
async def stream_async(
|
|
193
|
+
self,
|
|
194
|
+
cmd: List[str],
|
|
195
|
+
timeout: Optional[int] = None
|
|
196
|
+
) -> AsyncIterator[str]:
|
|
197
|
+
"""
|
|
198
|
+
Stream stdout lines from a command asynchronously.
|
|
199
|
+
|
|
200
|
+
Args:
|
|
201
|
+
cmd: Command and arguments as a list
|
|
202
|
+
timeout: Timeout in seconds for the entire operation
|
|
203
|
+
|
|
204
|
+
Yields:
|
|
205
|
+
str: Each line of output
|
|
206
|
+
"""
|
|
207
|
+
timeout = timeout or self.timeout
|
|
208
|
+
|
|
209
|
+
proc = await asyncio.create_subprocess_exec(
|
|
210
|
+
*cmd,
|
|
211
|
+
stdout=asyncio.subprocess.PIPE,
|
|
212
|
+
stderr=asyncio.subprocess.PIPE,
|
|
213
|
+
cwd=self.workspace,
|
|
214
|
+
env=self.get_env()
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
try:
|
|
218
|
+
async def read_lines():
|
|
219
|
+
while True:
|
|
220
|
+
line = await proc.stdout.readline()
|
|
221
|
+
if not line:
|
|
222
|
+
break
|
|
223
|
+
yield line.decode().rstrip('\n')
|
|
224
|
+
|
|
225
|
+
async for line in read_lines():
|
|
226
|
+
yield line
|
|
227
|
+
|
|
228
|
+
except asyncio.TimeoutError:
|
|
229
|
+
proc.kill()
|
|
230
|
+
await proc.wait()
|
|
231
|
+
raise TimeoutError(f"Stream timed out after {timeout}s")
|
|
232
|
+
finally:
|
|
233
|
+
if proc.returncode is None:
|
|
234
|
+
proc.kill()
|
|
235
|
+
await proc.wait()
|
|
236
|
+
|
|
237
|
+
def as_tool(self) -> callable:
|
|
238
|
+
"""
|
|
239
|
+
Return a callable suitable for use as a PraisonAI agent tool.
|
|
240
|
+
|
|
241
|
+
The returned function is synchronous and wraps the async execute method.
|
|
242
|
+
Uses asyncio.run() for proper event loop handling (Python 3.7+).
|
|
243
|
+
|
|
244
|
+
Returns:
|
|
245
|
+
callable: A function that can be used as an agent tool
|
|
246
|
+
"""
|
|
247
|
+
# Capture self for closure
|
|
248
|
+
integration = self
|
|
249
|
+
|
|
250
|
+
def tool_func(query: str) -> str:
|
|
251
|
+
"""Execute the CLI tool with the given query."""
|
|
252
|
+
# Use asyncio.run() for clean event loop management
|
|
253
|
+
# This creates a new event loop, runs the coroutine, and closes it
|
|
254
|
+
try:
|
|
255
|
+
# Check if we're already in an async context
|
|
256
|
+
asyncio.get_running_loop()
|
|
257
|
+
# If we're in an async context, use ThreadPoolExecutor to avoid nested loop
|
|
258
|
+
import concurrent.futures
|
|
259
|
+
with concurrent.futures.ThreadPoolExecutor() as executor:
|
|
260
|
+
future = executor.submit(asyncio.run, integration.execute(query))
|
|
261
|
+
return future.result()
|
|
262
|
+
except RuntimeError:
|
|
263
|
+
# No running loop, safe to use asyncio.run()
|
|
264
|
+
return asyncio.run(integration.execute(query))
|
|
265
|
+
|
|
266
|
+
# Set function metadata for agent tool registration
|
|
267
|
+
tool_func.__name__ = f"{self.cli_command}_tool"
|
|
268
|
+
tool_func.__doc__ = f"Execute {self.cli_command} for coding tasks."
|
|
269
|
+
|
|
270
|
+
return tool_func
|
|
271
|
+
|
|
272
|
+
def get_env(self) -> Dict[str, str]:
|
|
273
|
+
"""
|
|
274
|
+
Get environment variables for CLI execution.
|
|
275
|
+
|
|
276
|
+
Subclasses can override this to add tool-specific environment variables.
|
|
277
|
+
|
|
278
|
+
Returns:
|
|
279
|
+
dict: Environment variables to pass to the CLI
|
|
280
|
+
"""
|
|
281
|
+
return dict(os.environ)
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def get_available_integrations() -> Dict[str, bool]:
|
|
285
|
+
"""
|
|
286
|
+
Get a dictionary of all integrations and their availability status.
|
|
287
|
+
|
|
288
|
+
Returns:
|
|
289
|
+
dict: Mapping of integration name to availability (True/False)
|
|
290
|
+
"""
|
|
291
|
+
from .claude_code import ClaudeCodeIntegration
|
|
292
|
+
from .gemini_cli import GeminiCLIIntegration
|
|
293
|
+
from .codex_cli import CodexCLIIntegration
|
|
294
|
+
from .cursor_cli import CursorCLIIntegration
|
|
295
|
+
|
|
296
|
+
integrations = {
|
|
297
|
+
'claude': ClaudeCodeIntegration(),
|
|
298
|
+
'gemini': GeminiCLIIntegration(),
|
|
299
|
+
'codex': CodexCLIIntegration(),
|
|
300
|
+
'cursor': CursorCLIIntegration(),
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return {name: integration.is_available for name, integration in integrations.items()}
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Claude Code CLI Integration.
|
|
3
|
+
|
|
4
|
+
Provides integration with Claude Code CLI for AI-powered coding tasks.
|
|
5
|
+
|
|
6
|
+
Features:
|
|
7
|
+
- Headless mode execution with JSON output
|
|
8
|
+
- Session continuation support
|
|
9
|
+
- System prompt customization
|
|
10
|
+
- Tool restrictions
|
|
11
|
+
- Optional SDK integration (when claude-agent-sdk is installed)
|
|
12
|
+
|
|
13
|
+
Usage:
|
|
14
|
+
from praisonai.integrations import ClaudeCodeIntegration
|
|
15
|
+
|
|
16
|
+
# Create integration
|
|
17
|
+
claude = ClaudeCodeIntegration(workspace="/path/to/project")
|
|
18
|
+
|
|
19
|
+
# Execute a coding task
|
|
20
|
+
result = await claude.execute("Refactor the auth module")
|
|
21
|
+
|
|
22
|
+
# Stream output
|
|
23
|
+
async for event in claude.stream("Add error handling"):
|
|
24
|
+
print(event)
|
|
25
|
+
|
|
26
|
+
# Use as agent tool
|
|
27
|
+
tool = claude.as_tool()
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
import json
|
|
31
|
+
import os
|
|
32
|
+
from typing import AsyncIterator, Dict, Any, Optional, List
|
|
33
|
+
|
|
34
|
+
from .base import BaseCLIIntegration
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# Check if Claude Code SDK is available
|
|
38
|
+
CLAUDE_SDK_AVAILABLE = False
|
|
39
|
+
try:
|
|
40
|
+
from claude_agent_sdk import query as claude_query, ClaudeAgentOptions
|
|
41
|
+
CLAUDE_SDK_AVAILABLE = True
|
|
42
|
+
except ImportError:
|
|
43
|
+
pass
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class ClaudeCodeIntegration(BaseCLIIntegration):
|
|
47
|
+
"""
|
|
48
|
+
Integration with Claude Code CLI.
|
|
49
|
+
|
|
50
|
+
Supports both subprocess-based execution and SDK-based execution
|
|
51
|
+
(when claude-agent-sdk is installed).
|
|
52
|
+
|
|
53
|
+
Attributes:
|
|
54
|
+
output_format: Output format ("json", "text", "stream-json")
|
|
55
|
+
skip_permissions: Whether to skip permission prompts
|
|
56
|
+
system_prompt: Custom system prompt to append
|
|
57
|
+
allowed_tools: List of allowed tools (e.g., ["Read", "Write", "Bash"])
|
|
58
|
+
disallowed_tools: List of disallowed tools
|
|
59
|
+
use_sdk: Whether to use the SDK instead of subprocess
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
def __init__(
|
|
63
|
+
self,
|
|
64
|
+
workspace: str = ".",
|
|
65
|
+
timeout: int = 300,
|
|
66
|
+
output_format: str = "json",
|
|
67
|
+
skip_permissions: bool = True,
|
|
68
|
+
system_prompt: Optional[str] = None,
|
|
69
|
+
allowed_tools: Optional[List[str]] = None,
|
|
70
|
+
disallowed_tools: Optional[List[str]] = None,
|
|
71
|
+
use_sdk: bool = False,
|
|
72
|
+
model: Optional[str] = None,
|
|
73
|
+
):
|
|
74
|
+
"""
|
|
75
|
+
Initialize Claude Code integration.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
workspace: Working directory for CLI execution
|
|
79
|
+
timeout: Timeout in seconds for CLI execution
|
|
80
|
+
output_format: Output format ("json", "text", "stream-json")
|
|
81
|
+
skip_permissions: Whether to skip permission prompts (--dangerously-skip-permissions)
|
|
82
|
+
system_prompt: Custom system prompt to append
|
|
83
|
+
allowed_tools: List of allowed tools
|
|
84
|
+
disallowed_tools: List of disallowed tools
|
|
85
|
+
use_sdk: Whether to use the SDK instead of subprocess
|
|
86
|
+
model: Model to use (e.g., "sonnet", "opus")
|
|
87
|
+
"""
|
|
88
|
+
super().__init__(workspace=workspace, timeout=timeout)
|
|
89
|
+
|
|
90
|
+
self.output_format = output_format
|
|
91
|
+
self.skip_permissions = skip_permissions
|
|
92
|
+
self.system_prompt = system_prompt
|
|
93
|
+
self.allowed_tools = allowed_tools
|
|
94
|
+
self.disallowed_tools = disallowed_tools
|
|
95
|
+
self.use_sdk = use_sdk and CLAUDE_SDK_AVAILABLE
|
|
96
|
+
self.model = model
|
|
97
|
+
|
|
98
|
+
# Session management
|
|
99
|
+
self._session_active = False
|
|
100
|
+
|
|
101
|
+
@property
|
|
102
|
+
def cli_command(self) -> str:
|
|
103
|
+
"""Return the CLI command name."""
|
|
104
|
+
return "claude"
|
|
105
|
+
|
|
106
|
+
@property
|
|
107
|
+
def sdk_available(self) -> bool:
|
|
108
|
+
"""Check if the Claude Code SDK is available."""
|
|
109
|
+
return CLAUDE_SDK_AVAILABLE
|
|
110
|
+
|
|
111
|
+
def _build_command(
|
|
112
|
+
self,
|
|
113
|
+
prompt: str,
|
|
114
|
+
continue_session: bool = False,
|
|
115
|
+
**options
|
|
116
|
+
) -> List[str]:
|
|
117
|
+
"""
|
|
118
|
+
Build the Claude CLI command.
|
|
119
|
+
|
|
120
|
+
Args:
|
|
121
|
+
prompt: The prompt to send
|
|
122
|
+
continue_session: Whether to continue a previous session
|
|
123
|
+
**options: Additional options
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
List of command arguments
|
|
127
|
+
"""
|
|
128
|
+
cmd = ["claude"]
|
|
129
|
+
|
|
130
|
+
# Add print mode flag for non-interactive output
|
|
131
|
+
cmd.append("-p")
|
|
132
|
+
|
|
133
|
+
# Add output format
|
|
134
|
+
cmd.extend(["--output-format", self.output_format])
|
|
135
|
+
|
|
136
|
+
# Add continue flag if needed
|
|
137
|
+
if continue_session or self._session_active:
|
|
138
|
+
cmd.append("--continue")
|
|
139
|
+
|
|
140
|
+
# Add model if specified
|
|
141
|
+
if self.model:
|
|
142
|
+
cmd.extend(["--model", self.model])
|
|
143
|
+
|
|
144
|
+
# Add system prompt if specified
|
|
145
|
+
if self.system_prompt:
|
|
146
|
+
cmd.extend(["--append-system-prompt", self.system_prompt])
|
|
147
|
+
|
|
148
|
+
# Add allowed tools if specified
|
|
149
|
+
if self.allowed_tools:
|
|
150
|
+
cmd.extend(["--allowedTools", ",".join(self.allowed_tools)])
|
|
151
|
+
|
|
152
|
+
# Add disallowed tools if specified
|
|
153
|
+
if self.disallowed_tools:
|
|
154
|
+
cmd.extend(["--disallowedTools", ",".join(self.disallowed_tools)])
|
|
155
|
+
|
|
156
|
+
# Add verbose if needed
|
|
157
|
+
if options.get('verbose'):
|
|
158
|
+
cmd.append("--verbose")
|
|
159
|
+
|
|
160
|
+
# Add prompt last
|
|
161
|
+
cmd.append(prompt)
|
|
162
|
+
|
|
163
|
+
return cmd
|
|
164
|
+
|
|
165
|
+
async def execute(self, prompt: str, **options) -> str:
|
|
166
|
+
"""
|
|
167
|
+
Execute Claude Code CLI and return the result.
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
prompt: The prompt/query to send
|
|
171
|
+
**options: Additional options (continue_session, etc.)
|
|
172
|
+
|
|
173
|
+
Returns:
|
|
174
|
+
str: The CLI output (parsed from JSON if output_format is "json")
|
|
175
|
+
"""
|
|
176
|
+
if self.use_sdk:
|
|
177
|
+
return await self._execute_sdk(prompt, **options)
|
|
178
|
+
|
|
179
|
+
return await self._execute_subprocess(prompt, **options)
|
|
180
|
+
|
|
181
|
+
async def _execute_subprocess(self, prompt: str, **options) -> str:
|
|
182
|
+
"""Execute using subprocess."""
|
|
183
|
+
cmd = self._build_command(prompt, **options)
|
|
184
|
+
|
|
185
|
+
output = await self.execute_async(cmd)
|
|
186
|
+
|
|
187
|
+
# Mark session as active for continuation
|
|
188
|
+
self._session_active = True
|
|
189
|
+
|
|
190
|
+
# Parse JSON output if applicable
|
|
191
|
+
if self.output_format == "json":
|
|
192
|
+
try:
|
|
193
|
+
data = json.loads(output)
|
|
194
|
+
# Extract the main result
|
|
195
|
+
if isinstance(data, dict):
|
|
196
|
+
return data.get("result", data.get("content", str(data)))
|
|
197
|
+
return str(data)
|
|
198
|
+
except json.JSONDecodeError:
|
|
199
|
+
return output
|
|
200
|
+
|
|
201
|
+
return output
|
|
202
|
+
|
|
203
|
+
async def _execute_sdk(self, prompt: str, **options) -> str:
|
|
204
|
+
"""Execute using the Claude Code SDK."""
|
|
205
|
+
if not CLAUDE_SDK_AVAILABLE:
|
|
206
|
+
raise RuntimeError("Claude Code SDK not available. Install with: pip install claude-agent-sdk")
|
|
207
|
+
|
|
208
|
+
sdk_options = ClaudeAgentOptions(
|
|
209
|
+
cwd=self.workspace,
|
|
210
|
+
system_prompt=self.system_prompt,
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
if self.allowed_tools:
|
|
214
|
+
sdk_options.allowed_tools = self.allowed_tools
|
|
215
|
+
|
|
216
|
+
if self.skip_permissions:
|
|
217
|
+
sdk_options.permission_mode = 'acceptEdits'
|
|
218
|
+
|
|
219
|
+
result_parts = []
|
|
220
|
+
async for message in claude_query(prompt=prompt, options=sdk_options):
|
|
221
|
+
if hasattr(message, 'content'):
|
|
222
|
+
for block in message.content:
|
|
223
|
+
if hasattr(block, 'text'):
|
|
224
|
+
result_parts.append(block.text)
|
|
225
|
+
|
|
226
|
+
return '\n'.join(result_parts)
|
|
227
|
+
|
|
228
|
+
async def stream(self, prompt: str, **options) -> AsyncIterator[Dict[str, Any]]:
|
|
229
|
+
"""
|
|
230
|
+
Stream output from Claude Code CLI.
|
|
231
|
+
|
|
232
|
+
Args:
|
|
233
|
+
prompt: The prompt/query to send
|
|
234
|
+
**options: Additional options
|
|
235
|
+
|
|
236
|
+
Yields:
|
|
237
|
+
dict: Parsed JSON events from the CLI
|
|
238
|
+
"""
|
|
239
|
+
# Use stream-json format for streaming
|
|
240
|
+
original_format = self.output_format
|
|
241
|
+
self.output_format = "stream-json"
|
|
242
|
+
|
|
243
|
+
try:
|
|
244
|
+
cmd = self._build_command(prompt, **options)
|
|
245
|
+
|
|
246
|
+
async for line in self.stream_async(cmd):
|
|
247
|
+
if line.strip():
|
|
248
|
+
try:
|
|
249
|
+
event = json.loads(line)
|
|
250
|
+
yield event
|
|
251
|
+
except json.JSONDecodeError:
|
|
252
|
+
yield {"type": "text", "content": line}
|
|
253
|
+
finally:
|
|
254
|
+
self.output_format = original_format
|
|
255
|
+
|
|
256
|
+
def reset_session(self):
|
|
257
|
+
"""Reset the session state."""
|
|
258
|
+
self._session_active = False
|
|
259
|
+
|
|
260
|
+
def get_env(self) -> Dict[str, str]:
|
|
261
|
+
"""Get environment variables for CLI execution."""
|
|
262
|
+
env = super().get_env()
|
|
263
|
+
|
|
264
|
+
# Add Anthropic API key if available
|
|
265
|
+
if "ANTHROPIC_API_KEY" in os.environ:
|
|
266
|
+
env["ANTHROPIC_API_KEY"] = os.environ["ANTHROPIC_API_KEY"]
|
|
267
|
+
elif "CLAUDE_API_KEY" in os.environ:
|
|
268
|
+
env["ANTHROPIC_API_KEY"] = os.environ["CLAUDE_API_KEY"]
|
|
269
|
+
|
|
270
|
+
return env
|