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,651 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Git Integration System for PraisonAI CLI.
|
|
3
|
+
|
|
4
|
+
Inspired by Aider's Git integration for auto-commits and change tracking.
|
|
5
|
+
Provides seamless Git operations with AI-generated commit messages.
|
|
6
|
+
|
|
7
|
+
Architecture:
|
|
8
|
+
- GitManager: Core Git operations using subprocess
|
|
9
|
+
- CommitGenerator: AI-powered commit message generation
|
|
10
|
+
- DiffViewer: Rich diff display
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from dataclasses import dataclass, field
|
|
14
|
+
from typing import List, Optional
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
import subprocess
|
|
17
|
+
import logging
|
|
18
|
+
import re
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# ============================================================================
|
|
24
|
+
# Data Classes
|
|
25
|
+
# ============================================================================
|
|
26
|
+
|
|
27
|
+
@dataclass
|
|
28
|
+
class GitStatus:
|
|
29
|
+
"""Git repository status."""
|
|
30
|
+
branch: str = ""
|
|
31
|
+
is_clean: bool = True
|
|
32
|
+
staged_files: List[str] = field(default_factory=list)
|
|
33
|
+
modified_files: List[str] = field(default_factory=list)
|
|
34
|
+
untracked_files: List[str] = field(default_factory=list)
|
|
35
|
+
ahead: int = 0
|
|
36
|
+
behind: int = 0
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
def has_changes(self) -> bool:
|
|
40
|
+
"""Check if there are any changes."""
|
|
41
|
+
return bool(self.staged_files or self.modified_files or self.untracked_files)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass
|
|
45
|
+
class GitCommit:
|
|
46
|
+
"""Git commit information."""
|
|
47
|
+
hash: str
|
|
48
|
+
short_hash: str
|
|
49
|
+
message: str
|
|
50
|
+
author: str
|
|
51
|
+
date: str
|
|
52
|
+
files_changed: int = 0
|
|
53
|
+
insertions: int = 0
|
|
54
|
+
deletions: int = 0
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclass
|
|
58
|
+
class GitDiff:
|
|
59
|
+
"""Git diff information."""
|
|
60
|
+
file_path: str
|
|
61
|
+
additions: int = 0
|
|
62
|
+
deletions: int = 0
|
|
63
|
+
content: str = ""
|
|
64
|
+
is_new: bool = False
|
|
65
|
+
is_deleted: bool = False
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
# ============================================================================
|
|
69
|
+
# Git Manager
|
|
70
|
+
# ============================================================================
|
|
71
|
+
|
|
72
|
+
class GitManager:
|
|
73
|
+
"""
|
|
74
|
+
Manages Git operations using subprocess.
|
|
75
|
+
|
|
76
|
+
Uses subprocess instead of GitPython for lighter dependencies.
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
def __init__(self, repo_path: Optional[str] = None, verbose: bool = False):
|
|
80
|
+
self.repo_path = Path(repo_path) if repo_path else Path.cwd()
|
|
81
|
+
self.verbose = verbose
|
|
82
|
+
self._git_available = self._check_git()
|
|
83
|
+
|
|
84
|
+
def _check_git(self) -> bool:
|
|
85
|
+
"""Check if git is available."""
|
|
86
|
+
try:
|
|
87
|
+
result = subprocess.run(
|
|
88
|
+
["git", "--version"],
|
|
89
|
+
capture_output=True,
|
|
90
|
+
text=True,
|
|
91
|
+
cwd=self.repo_path
|
|
92
|
+
)
|
|
93
|
+
return result.returncode == 0
|
|
94
|
+
except FileNotFoundError:
|
|
95
|
+
return False
|
|
96
|
+
|
|
97
|
+
def _run_git(self, *args: str, check: bool = True) -> subprocess.CompletedProcess:
|
|
98
|
+
"""Run a git command."""
|
|
99
|
+
cmd = ["git"] + list(args)
|
|
100
|
+
if self.verbose:
|
|
101
|
+
logger.debug(f"Running: {' '.join(cmd)}")
|
|
102
|
+
|
|
103
|
+
result = subprocess.run(
|
|
104
|
+
cmd,
|
|
105
|
+
capture_output=True,
|
|
106
|
+
text=True,
|
|
107
|
+
cwd=self.repo_path
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
if check and result.returncode != 0:
|
|
111
|
+
logger.debug(f"Git error: {result.stderr}")
|
|
112
|
+
|
|
113
|
+
return result
|
|
114
|
+
|
|
115
|
+
@property
|
|
116
|
+
def is_repo(self) -> bool:
|
|
117
|
+
"""Check if current directory is a git repository."""
|
|
118
|
+
if not self._git_available:
|
|
119
|
+
return False
|
|
120
|
+
result = self._run_git("rev-parse", "--git-dir", check=False)
|
|
121
|
+
return result.returncode == 0
|
|
122
|
+
|
|
123
|
+
def get_status(self) -> GitStatus:
|
|
124
|
+
"""Get repository status."""
|
|
125
|
+
status = GitStatus()
|
|
126
|
+
|
|
127
|
+
if not self.is_repo:
|
|
128
|
+
return status
|
|
129
|
+
|
|
130
|
+
# Get branch
|
|
131
|
+
result = self._run_git("branch", "--show-current", check=False)
|
|
132
|
+
if result.returncode == 0:
|
|
133
|
+
status.branch = result.stdout.strip()
|
|
134
|
+
|
|
135
|
+
# Get status
|
|
136
|
+
result = self._run_git("status", "--porcelain", check=False)
|
|
137
|
+
if result.returncode == 0:
|
|
138
|
+
for line in result.stdout.strip().split("\n"):
|
|
139
|
+
if not line:
|
|
140
|
+
continue
|
|
141
|
+
|
|
142
|
+
status_code = line[:2]
|
|
143
|
+
file_path = line[3:]
|
|
144
|
+
|
|
145
|
+
if status_code[0] in "MADRCU":
|
|
146
|
+
status.staged_files.append(file_path)
|
|
147
|
+
if status_code[1] in "MD":
|
|
148
|
+
status.modified_files.append(file_path)
|
|
149
|
+
if status_code == "??":
|
|
150
|
+
status.untracked_files.append(file_path)
|
|
151
|
+
|
|
152
|
+
status.is_clean = not status.has_changes
|
|
153
|
+
|
|
154
|
+
# Get ahead/behind
|
|
155
|
+
result = self._run_git("rev-list", "--left-right", "--count", "HEAD...@{u}", check=False)
|
|
156
|
+
if result.returncode == 0:
|
|
157
|
+
parts = result.stdout.strip().split()
|
|
158
|
+
if len(parts) == 2:
|
|
159
|
+
status.ahead = int(parts[0])
|
|
160
|
+
status.behind = int(parts[1])
|
|
161
|
+
|
|
162
|
+
return status
|
|
163
|
+
|
|
164
|
+
def get_diff(self, staged: bool = False, file_path: Optional[str] = None) -> List[GitDiff]:
|
|
165
|
+
"""Get diff information."""
|
|
166
|
+
diffs = []
|
|
167
|
+
|
|
168
|
+
if not self.is_repo:
|
|
169
|
+
return diffs
|
|
170
|
+
|
|
171
|
+
args = ["diff"]
|
|
172
|
+
if staged:
|
|
173
|
+
args.append("--staged")
|
|
174
|
+
args.append("--numstat")
|
|
175
|
+
|
|
176
|
+
if file_path:
|
|
177
|
+
args.append("--")
|
|
178
|
+
args.append(file_path)
|
|
179
|
+
|
|
180
|
+
result = self._run_git(*args, check=False)
|
|
181
|
+
if result.returncode != 0:
|
|
182
|
+
return diffs
|
|
183
|
+
|
|
184
|
+
for line in result.stdout.strip().split("\n"):
|
|
185
|
+
if not line:
|
|
186
|
+
continue
|
|
187
|
+
|
|
188
|
+
parts = line.split("\t")
|
|
189
|
+
if len(parts) >= 3:
|
|
190
|
+
additions = int(parts[0]) if parts[0] != "-" else 0
|
|
191
|
+
deletions = int(parts[1]) if parts[1] != "-" else 0
|
|
192
|
+
path = parts[2]
|
|
193
|
+
|
|
194
|
+
diffs.append(GitDiff(
|
|
195
|
+
file_path=path,
|
|
196
|
+
additions=additions,
|
|
197
|
+
deletions=deletions
|
|
198
|
+
))
|
|
199
|
+
|
|
200
|
+
return diffs
|
|
201
|
+
|
|
202
|
+
def get_diff_content(self, staged: bool = False, file_path: Optional[str] = None) -> str:
|
|
203
|
+
"""Get full diff content."""
|
|
204
|
+
if not self.is_repo:
|
|
205
|
+
return ""
|
|
206
|
+
|
|
207
|
+
args = ["diff"]
|
|
208
|
+
if staged:
|
|
209
|
+
args.append("--staged")
|
|
210
|
+
|
|
211
|
+
if file_path:
|
|
212
|
+
args.append("--")
|
|
213
|
+
args.append(file_path)
|
|
214
|
+
|
|
215
|
+
result = self._run_git(*args, check=False)
|
|
216
|
+
return result.stdout if result.returncode == 0 else ""
|
|
217
|
+
|
|
218
|
+
def stage_files(self, files: Optional[List[str]] = None) -> bool:
|
|
219
|
+
"""Stage files for commit."""
|
|
220
|
+
if not self.is_repo:
|
|
221
|
+
return False
|
|
222
|
+
|
|
223
|
+
if files:
|
|
224
|
+
result = self._run_git("add", *files, check=False)
|
|
225
|
+
else:
|
|
226
|
+
result = self._run_git("add", "-A", check=False)
|
|
227
|
+
|
|
228
|
+
return result.returncode == 0
|
|
229
|
+
|
|
230
|
+
def commit(self, message: str, allow_empty: bool = False) -> Optional[GitCommit]:
|
|
231
|
+
"""Create a commit."""
|
|
232
|
+
if not self.is_repo:
|
|
233
|
+
return None
|
|
234
|
+
|
|
235
|
+
args = ["commit", "-m", message]
|
|
236
|
+
if allow_empty:
|
|
237
|
+
args.append("--allow-empty")
|
|
238
|
+
|
|
239
|
+
result = self._run_git(*args, check=False)
|
|
240
|
+
|
|
241
|
+
if result.returncode != 0:
|
|
242
|
+
logger.debug(f"Commit failed: {result.stderr}")
|
|
243
|
+
return None
|
|
244
|
+
|
|
245
|
+
# Get commit info
|
|
246
|
+
return self.get_last_commit()
|
|
247
|
+
|
|
248
|
+
def get_last_commit(self) -> Optional[GitCommit]:
|
|
249
|
+
"""Get the last commit."""
|
|
250
|
+
if not self.is_repo:
|
|
251
|
+
return None
|
|
252
|
+
|
|
253
|
+
result = self._run_git(
|
|
254
|
+
"log", "-1",
|
|
255
|
+
"--format=%H|%h|%s|%an|%ai",
|
|
256
|
+
check=False
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
if result.returncode != 0:
|
|
260
|
+
return None
|
|
261
|
+
|
|
262
|
+
parts = result.stdout.strip().split("|")
|
|
263
|
+
if len(parts) >= 5:
|
|
264
|
+
return GitCommit(
|
|
265
|
+
hash=parts[0],
|
|
266
|
+
short_hash=parts[1],
|
|
267
|
+
message=parts[2],
|
|
268
|
+
author=parts[3],
|
|
269
|
+
date=parts[4]
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
return None
|
|
273
|
+
|
|
274
|
+
def get_log(self, count: int = 10) -> List[GitCommit]:
|
|
275
|
+
"""Get commit log."""
|
|
276
|
+
commits = []
|
|
277
|
+
|
|
278
|
+
if not self.is_repo:
|
|
279
|
+
return commits
|
|
280
|
+
|
|
281
|
+
result = self._run_git(
|
|
282
|
+
"log", f"-{count}",
|
|
283
|
+
"--format=%H|%h|%s|%an|%ai",
|
|
284
|
+
check=False
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
if result.returncode != 0:
|
|
288
|
+
return commits
|
|
289
|
+
|
|
290
|
+
for line in result.stdout.strip().split("\n"):
|
|
291
|
+
if not line:
|
|
292
|
+
continue
|
|
293
|
+
|
|
294
|
+
parts = line.split("|")
|
|
295
|
+
if len(parts) >= 5:
|
|
296
|
+
commits.append(GitCommit(
|
|
297
|
+
hash=parts[0],
|
|
298
|
+
short_hash=parts[1],
|
|
299
|
+
message=parts[2],
|
|
300
|
+
author=parts[3],
|
|
301
|
+
date=parts[4]
|
|
302
|
+
))
|
|
303
|
+
|
|
304
|
+
return commits
|
|
305
|
+
|
|
306
|
+
def undo_last_commit(self, soft: bool = True) -> bool:
|
|
307
|
+
"""Undo the last commit."""
|
|
308
|
+
if not self.is_repo:
|
|
309
|
+
return False
|
|
310
|
+
|
|
311
|
+
reset_type = "--soft" if soft else "--hard"
|
|
312
|
+
result = self._run_git("reset", reset_type, "HEAD~1", check=False)
|
|
313
|
+
|
|
314
|
+
return result.returncode == 0
|
|
315
|
+
|
|
316
|
+
def create_branch(self, name: str, checkout: bool = True) -> bool:
|
|
317
|
+
"""Create a new branch."""
|
|
318
|
+
if not self.is_repo:
|
|
319
|
+
return False
|
|
320
|
+
|
|
321
|
+
if checkout:
|
|
322
|
+
result = self._run_git("checkout", "-b", name, check=False)
|
|
323
|
+
else:
|
|
324
|
+
result = self._run_git("branch", name, check=False)
|
|
325
|
+
|
|
326
|
+
return result.returncode == 0
|
|
327
|
+
|
|
328
|
+
def checkout_branch(self, name: str) -> bool:
|
|
329
|
+
"""Checkout a branch."""
|
|
330
|
+
if not self.is_repo:
|
|
331
|
+
return False
|
|
332
|
+
|
|
333
|
+
result = self._run_git("checkout", name, check=False)
|
|
334
|
+
return result.returncode == 0
|
|
335
|
+
|
|
336
|
+
def get_branches(self) -> List[str]:
|
|
337
|
+
"""Get list of branches."""
|
|
338
|
+
if not self.is_repo:
|
|
339
|
+
return []
|
|
340
|
+
|
|
341
|
+
result = self._run_git("branch", "--list", check=False)
|
|
342
|
+
if result.returncode != 0:
|
|
343
|
+
return []
|
|
344
|
+
|
|
345
|
+
branches = []
|
|
346
|
+
for line in result.stdout.strip().split("\n"):
|
|
347
|
+
if line:
|
|
348
|
+
# Remove * and whitespace
|
|
349
|
+
branch = line.strip().lstrip("* ")
|
|
350
|
+
branches.append(branch)
|
|
351
|
+
|
|
352
|
+
return branches
|
|
353
|
+
|
|
354
|
+
def stash(self, message: Optional[str] = None) -> bool:
|
|
355
|
+
"""Stash changes."""
|
|
356
|
+
if not self.is_repo:
|
|
357
|
+
return False
|
|
358
|
+
|
|
359
|
+
args = ["stash", "push"]
|
|
360
|
+
if message:
|
|
361
|
+
args.extend(["-m", message])
|
|
362
|
+
|
|
363
|
+
result = self._run_git(*args, check=False)
|
|
364
|
+
return result.returncode == 0
|
|
365
|
+
|
|
366
|
+
def stash_pop(self) -> bool:
|
|
367
|
+
"""Pop stashed changes."""
|
|
368
|
+
if not self.is_repo:
|
|
369
|
+
return False
|
|
370
|
+
|
|
371
|
+
result = self._run_git("stash", "pop", check=False)
|
|
372
|
+
return result.returncode == 0
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
# ============================================================================
|
|
376
|
+
# Commit Message Generator
|
|
377
|
+
# ============================================================================
|
|
378
|
+
|
|
379
|
+
class CommitMessageGenerator:
|
|
380
|
+
"""
|
|
381
|
+
Generates commit messages using AI or templates.
|
|
382
|
+
"""
|
|
383
|
+
|
|
384
|
+
def __init__(self, use_ai: bool = True):
|
|
385
|
+
self.use_ai = use_ai
|
|
386
|
+
|
|
387
|
+
def generate(
|
|
388
|
+
self,
|
|
389
|
+
diff_content: str,
|
|
390
|
+
context: Optional[str] = None,
|
|
391
|
+
style: str = "conventional"
|
|
392
|
+
) -> str:
|
|
393
|
+
"""
|
|
394
|
+
Generate a commit message.
|
|
395
|
+
|
|
396
|
+
Args:
|
|
397
|
+
diff_content: The diff to describe
|
|
398
|
+
context: Optional context about the changes
|
|
399
|
+
style: Message style (conventional, simple, detailed)
|
|
400
|
+
|
|
401
|
+
Returns:
|
|
402
|
+
Generated commit message
|
|
403
|
+
"""
|
|
404
|
+
if not diff_content:
|
|
405
|
+
return "Empty commit"
|
|
406
|
+
|
|
407
|
+
if self.use_ai:
|
|
408
|
+
return self._generate_with_ai(diff_content, context, style)
|
|
409
|
+
else:
|
|
410
|
+
return self._generate_from_diff(diff_content, style)
|
|
411
|
+
|
|
412
|
+
def _generate_with_ai(
|
|
413
|
+
self,
|
|
414
|
+
diff_content: str,
|
|
415
|
+
context: Optional[str],
|
|
416
|
+
style: str
|
|
417
|
+
) -> str:
|
|
418
|
+
"""Generate commit message using AI."""
|
|
419
|
+
# This would integrate with the agent system
|
|
420
|
+
# For now, return a template-based message
|
|
421
|
+
return self._generate_from_diff(diff_content, style)
|
|
422
|
+
|
|
423
|
+
def _generate_from_diff(self, diff_content: str, style: str) -> str:
|
|
424
|
+
"""Generate commit message from diff analysis."""
|
|
425
|
+
# Analyze diff
|
|
426
|
+
files_changed = set()
|
|
427
|
+
additions = 0
|
|
428
|
+
deletions = 0
|
|
429
|
+
|
|
430
|
+
for line in diff_content.split("\n"):
|
|
431
|
+
if line.startswith("diff --git"):
|
|
432
|
+
match = re.search(r"b/(.+)$", line)
|
|
433
|
+
if match:
|
|
434
|
+
files_changed.add(match.group(1))
|
|
435
|
+
elif line.startswith("+") and not line.startswith("+++"):
|
|
436
|
+
additions += 1
|
|
437
|
+
elif line.startswith("-") and not line.startswith("---"):
|
|
438
|
+
deletions += 1
|
|
439
|
+
|
|
440
|
+
# Determine change type
|
|
441
|
+
if additions > deletions * 2:
|
|
442
|
+
change_type = "feat"
|
|
443
|
+
action = "Add"
|
|
444
|
+
elif deletions > additions * 2:
|
|
445
|
+
change_type = "refactor"
|
|
446
|
+
action = "Remove"
|
|
447
|
+
else:
|
|
448
|
+
change_type = "fix"
|
|
449
|
+
action = "Update"
|
|
450
|
+
|
|
451
|
+
# Build message
|
|
452
|
+
if len(files_changed) == 1:
|
|
453
|
+
file_name = list(files_changed)[0]
|
|
454
|
+
scope = Path(file_name).stem
|
|
455
|
+
message = f"{change_type}({scope}): {action} {file_name}"
|
|
456
|
+
elif len(files_changed) <= 3:
|
|
457
|
+
files_str = ", ".join(sorted(files_changed))
|
|
458
|
+
message = f"{change_type}: {action} {files_str}"
|
|
459
|
+
else:
|
|
460
|
+
message = f"{change_type}: {action} {len(files_changed)} files"
|
|
461
|
+
|
|
462
|
+
if style == "detailed":
|
|
463
|
+
message += f"\n\n+{additions} -{deletions} lines"
|
|
464
|
+
|
|
465
|
+
return message
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
# ============================================================================
|
|
469
|
+
# Diff Viewer
|
|
470
|
+
# ============================================================================
|
|
471
|
+
|
|
472
|
+
class DiffViewer:
|
|
473
|
+
"""
|
|
474
|
+
Rich-based diff viewer.
|
|
475
|
+
"""
|
|
476
|
+
|
|
477
|
+
def __init__(self):
|
|
478
|
+
self._console = None
|
|
479
|
+
|
|
480
|
+
@property
|
|
481
|
+
def console(self):
|
|
482
|
+
"""Lazy load Rich console."""
|
|
483
|
+
if self._console is None:
|
|
484
|
+
try:
|
|
485
|
+
from rich.console import Console
|
|
486
|
+
self._console = Console()
|
|
487
|
+
except ImportError:
|
|
488
|
+
self._console = None
|
|
489
|
+
return self._console
|
|
490
|
+
|
|
491
|
+
def display_diff(self, diff_content: str, title: str = "Changes") -> None:
|
|
492
|
+
"""Display diff with syntax highlighting."""
|
|
493
|
+
if not self.console:
|
|
494
|
+
print(diff_content)
|
|
495
|
+
return
|
|
496
|
+
|
|
497
|
+
from rich.panel import Panel
|
|
498
|
+
from rich.syntax import Syntax
|
|
499
|
+
|
|
500
|
+
syntax = Syntax(diff_content, "diff", theme="monokai", line_numbers=True)
|
|
501
|
+
self.console.print(Panel(syntax, title=title, border_style="blue"))
|
|
502
|
+
|
|
503
|
+
def display_status(self, status: GitStatus) -> None:
|
|
504
|
+
"""Display git status."""
|
|
505
|
+
if not self.console:
|
|
506
|
+
print(f"Branch: {status.branch}")
|
|
507
|
+
print(f"Staged: {len(status.staged_files)}")
|
|
508
|
+
print(f"Modified: {len(status.modified_files)}")
|
|
509
|
+
print(f"Untracked: {len(status.untracked_files)}")
|
|
510
|
+
return
|
|
511
|
+
|
|
512
|
+
from rich.panel import Panel
|
|
513
|
+
from rich.table import Table
|
|
514
|
+
|
|
515
|
+
table = Table(show_header=True, header_style="bold cyan")
|
|
516
|
+
table.add_column("Category")
|
|
517
|
+
table.add_column("Files", justify="right")
|
|
518
|
+
|
|
519
|
+
table.add_row("Branch", status.branch or "detached")
|
|
520
|
+
table.add_row("Staged", str(len(status.staged_files)))
|
|
521
|
+
table.add_row("Modified", str(len(status.modified_files)))
|
|
522
|
+
table.add_row("Untracked", str(len(status.untracked_files)))
|
|
523
|
+
|
|
524
|
+
if status.ahead or status.behind:
|
|
525
|
+
table.add_row("Ahead/Behind", f"+{status.ahead} / -{status.behind}")
|
|
526
|
+
|
|
527
|
+
self.console.print(Panel(table, title="📊 Git Status", border_style="green"))
|
|
528
|
+
|
|
529
|
+
def display_log(self, commits: List[GitCommit]) -> None:
|
|
530
|
+
"""Display commit log."""
|
|
531
|
+
if not self.console:
|
|
532
|
+
for commit in commits:
|
|
533
|
+
print(f"{commit.short_hash} {commit.message}")
|
|
534
|
+
return
|
|
535
|
+
|
|
536
|
+
from rich.table import Table
|
|
537
|
+
|
|
538
|
+
table = Table(show_header=True, header_style="bold cyan")
|
|
539
|
+
table.add_column("Hash")
|
|
540
|
+
table.add_column("Message")
|
|
541
|
+
table.add_column("Author")
|
|
542
|
+
table.add_column("Date")
|
|
543
|
+
|
|
544
|
+
for commit in commits:
|
|
545
|
+
table.add_row(
|
|
546
|
+
commit.short_hash,
|
|
547
|
+
commit.message[:50] + "..." if len(commit.message) > 50 else commit.message,
|
|
548
|
+
commit.author,
|
|
549
|
+
commit.date[:10]
|
|
550
|
+
)
|
|
551
|
+
|
|
552
|
+
self.console.print(table)
|
|
553
|
+
|
|
554
|
+
|
|
555
|
+
# ============================================================================
|
|
556
|
+
# CLI Integration Handler
|
|
557
|
+
# ============================================================================
|
|
558
|
+
|
|
559
|
+
class GitIntegrationHandler:
|
|
560
|
+
"""
|
|
561
|
+
Handler for integrating Git with PraisonAI CLI.
|
|
562
|
+
"""
|
|
563
|
+
|
|
564
|
+
def __init__(self, verbose: bool = False):
|
|
565
|
+
self.verbose = verbose
|
|
566
|
+
self._git: Optional[GitManager] = None
|
|
567
|
+
self._viewer: Optional[DiffViewer] = None
|
|
568
|
+
self._generator: Optional[CommitMessageGenerator] = None
|
|
569
|
+
|
|
570
|
+
@property
|
|
571
|
+
def feature_name(self) -> str:
|
|
572
|
+
return "git_integration"
|
|
573
|
+
|
|
574
|
+
def initialize(self, repo_path: Optional[str] = None) -> GitManager:
|
|
575
|
+
"""Initialize Git manager."""
|
|
576
|
+
self._git = GitManager(repo_path=repo_path, verbose=self.verbose)
|
|
577
|
+
self._viewer = DiffViewer()
|
|
578
|
+
self._generator = CommitMessageGenerator()
|
|
579
|
+
|
|
580
|
+
if self.verbose and self._git.is_repo:
|
|
581
|
+
from rich import print as rprint
|
|
582
|
+
status = self._git.get_status()
|
|
583
|
+
rprint(f"[cyan]Git initialized on branch: {status.branch}[/cyan]")
|
|
584
|
+
|
|
585
|
+
return self._git
|
|
586
|
+
|
|
587
|
+
def get_git(self) -> Optional[GitManager]:
|
|
588
|
+
"""Get the Git manager."""
|
|
589
|
+
return self._git
|
|
590
|
+
|
|
591
|
+
def show_status(self) -> GitStatus:
|
|
592
|
+
"""Show git status."""
|
|
593
|
+
if not self._git:
|
|
594
|
+
self._git = self.initialize()
|
|
595
|
+
|
|
596
|
+
status = self._git.get_status()
|
|
597
|
+
if self._viewer:
|
|
598
|
+
self._viewer.display_status(status)
|
|
599
|
+
|
|
600
|
+
return status
|
|
601
|
+
|
|
602
|
+
def show_diff(self, staged: bool = False) -> str:
|
|
603
|
+
"""Show diff."""
|
|
604
|
+
if not self._git:
|
|
605
|
+
self._git = self.initialize()
|
|
606
|
+
|
|
607
|
+
diff_content = self._git.get_diff_content(staged=staged)
|
|
608
|
+
if self._viewer and diff_content:
|
|
609
|
+
self._viewer.display_diff(diff_content)
|
|
610
|
+
|
|
611
|
+
return diff_content
|
|
612
|
+
|
|
613
|
+
def commit(
|
|
614
|
+
self,
|
|
615
|
+
message: Optional[str] = None,
|
|
616
|
+
auto_stage: bool = True
|
|
617
|
+
) -> Optional[GitCommit]:
|
|
618
|
+
"""Create a commit."""
|
|
619
|
+
if not self._git:
|
|
620
|
+
self._git = self.initialize()
|
|
621
|
+
|
|
622
|
+
if auto_stage:
|
|
623
|
+
self._git.stage_files()
|
|
624
|
+
|
|
625
|
+
if not message:
|
|
626
|
+
# Generate message from diff
|
|
627
|
+
diff_content = self._git.get_diff_content(staged=True)
|
|
628
|
+
if self._generator:
|
|
629
|
+
message = self._generator.generate(diff_content)
|
|
630
|
+
else:
|
|
631
|
+
message = "Update files"
|
|
632
|
+
|
|
633
|
+
return self._git.commit(message)
|
|
634
|
+
|
|
635
|
+
def undo(self, soft: bool = True) -> bool:
|
|
636
|
+
"""Undo last commit."""
|
|
637
|
+
if not self._git:
|
|
638
|
+
self._git = self.initialize()
|
|
639
|
+
|
|
640
|
+
return self._git.undo_last_commit(soft=soft)
|
|
641
|
+
|
|
642
|
+
def show_log(self, count: int = 10) -> List[GitCommit]:
|
|
643
|
+
"""Show commit log."""
|
|
644
|
+
if not self._git:
|
|
645
|
+
self._git = self.initialize()
|
|
646
|
+
|
|
647
|
+
commits = self._git.get_log(count=count)
|
|
648
|
+
if self._viewer:
|
|
649
|
+
self._viewer.display_log(commits)
|
|
650
|
+
|
|
651
|
+
return commits
|