aegis-stack 0.2.0rc2__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.
- aegis/__init__.py +5 -0
- aegis/__main__.py +51 -0
- aegis/cli/__init__.py +6 -0
- aegis/cli/callbacks.py +114 -0
- aegis/cli/interactive.py +611 -0
- aegis/cli/utils.py +70 -0
- aegis/cli/validators.py +34 -0
- aegis/commands/__init__.py +6 -0
- aegis/commands/add.py +353 -0
- aegis/commands/add_service.py +332 -0
- aegis/commands/components.py +35 -0
- aegis/commands/init.py +370 -0
- aegis/commands/remove.py +227 -0
- aegis/commands/services.py +52 -0
- aegis/commands/update.py +252 -0
- aegis/commands/version.py +12 -0
- aegis/config/__init__.py +1 -0
- aegis/config/shared_files.py +136 -0
- aegis/core/CLAUDE.md +377 -0
- aegis/core/__init__.py +6 -0
- aegis/core/component_files.py +228 -0
- aegis/core/component_utils.py +220 -0
- aegis/core/components.py +127 -0
- aegis/core/copier_manager.py +315 -0
- aegis/core/copier_updater.py +475 -0
- aegis/core/dependency_resolver.py +119 -0
- aegis/core/manual_updater.py +554 -0
- aegis/core/post_gen_tasks.py +547 -0
- aegis/core/service_resolver.py +261 -0
- aegis/core/services.py +157 -0
- aegis/core/template_generator.py +266 -0
- aegis/core/version_compatibility.py +259 -0
- aegis/templates/CLAUDE.md +591 -0
- aegis/templates/cookiecutter-aegis-project/cookiecutter.json +39 -0
- aegis/templates/cookiecutter-aegis-project/hooks/post_gen_project.py +214 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/.dockerignore +71 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/.env.example.j2 +130 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/.gitignore +131 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/Dockerfile +53 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/Makefile +236 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/README.md.j2 +196 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/alembic/alembic.ini.j2 +111 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/alembic/env.py.j2 +91 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/alembic/script.py.mako +25 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/alembic/versions/001_initial_auth.py.j2 +51 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/__init__.py +5 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/__init__.py +6 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/ai.py.j2 +700 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/ai_rendering.py +361 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/auth.py.j2 +253 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/health.py.j2 +419 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/load_test.py.j2 +656 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/main.py.j2 +65 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/marko_terminal_renderer.py +489 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/tasks.py.j2 +328 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/{% if cookiecutter.include_scheduler == /"yes/" %}tasks.py{% endif %}" +340 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/__init__.py +0 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/__init__.py +0 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/ai/__init__.py +8 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/ai/router.py +329 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/auth/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/auth/router.py +64 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/deps.py +58 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/health.py +163 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/models.py.j2 +280 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/routing.py.j2 +32 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/scheduler.py.j2 +121 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/worker.py.j2 +478 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/hooks.py +144 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/main.py +31 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/middleware/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/middleware/cors.py +20 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/shutdown/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/shutdown/cleanup.py +14 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/startup/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/startup/component_health.py.j2 +418 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/startup/database_init.py.j2 +83 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/__init__.py +5 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/controls/__init__.py +27 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/controls/table.py +78 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/controls/text.py +142 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/__init__.py.j2 +47 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/ai_card.py +287 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/auth_card.py +198 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/base_card.py +256 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/card_factory.py +227 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/card_utils.py +333 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/database_card.py +420 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/fastapi_card.py +328 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/flet_card.py +267 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/redis_card.py +322 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/scheduler_card.py +352 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/services_card.py +233 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/worker_card.py +684 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/main.py.j2 +653 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/theme.py +48 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/scheduler/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/scheduler/main.py.j2 +156 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/CLAUDE.md.j2 +213 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/__init__.py +6 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/constants.py.j2 +30 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/pools.py +97 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/queues/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/queues/load_test.py +55 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/queues/media.py +49 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/queues/system.py +44 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/registry.py +139 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/tasks/__init__.py +120 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/tasks/load_tasks.py +507 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/tasks/simple_system_tasks.py +33 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/tasks/system_tasks.py +281 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/core/config.py.j2 +178 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/core/constants.py +58 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/core/db.py.j2 +176 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/core/log.py +92 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/core/security.py +62 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/entrypoints/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/entrypoints/webserver.py +40 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/entrypoints/{% if cookiecutter.include_scheduler == /"yes/" %}scheduler.py{% endif %}" +21 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/integrations/__init__.py +0 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/integrations/main.py +62 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/models/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/models/user.py +44 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/py.typed +0 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/__init__.py +8 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/config.py +130 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/conversation.py +213 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/health.py +96 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/models.py +229 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/providers.py +370 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/service.py +388 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/auth/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/auth/auth_service.py +41 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/auth/health.py +164 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/auth/user_service.py +83 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/backend/middleware_inspector.py.j2 +223 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/backend/models.py.j2 +70 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/backend/route_inspector.py.j2 +155 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/load_test.py +679 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/load_test_models.py +266 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/scheduler/__init__.py.j2 +21 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/scheduler/models.py.j2 +119 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/scheduler/scheduled_task_manager.py.j2 +273 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/scheduler/task_monitor.py.j2 +189 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/shared/__init__.py +15 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/shared/models.py +26 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/__init__.py +52 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/alerts.py +94 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/backup.py.j2 +119 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/health.py.j2 +1333 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/models.py +243 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/ui.py +52 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/assets/aegis-manifesto-dark.png +0 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/assets/aegis-manifesto-square-backup.png +0 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/assets/aegis-manifesto.png +0 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/.dockerignore +71 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/.env.example.j2 +64 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/.gitignore +131 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/Dockerfile +53 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/Makefile +211 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/README.md.j2 +172 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/docker-compose.yml.j2 +78 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/mkdocs.yml.j2 +62 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/pyproject.toml.j2 +120 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/uv.lock +1673 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docker-compose.yml.j2 +200 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/api.md +191 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/components/scheduler.md +0 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/components/scheduler.md.j2 +621 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/development.md +215 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/health.md +240 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/javascripts/mermaid-config.js +62 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/stylesheets/mermaid.css +95 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/mkdocs.yml.j2 +62 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/pyproject.toml.j2 +131 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/scripts/entrypoint.sh +87 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/scripts/entrypoint.sh.j2 +93 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/scripts/gen_docs.py +16 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/api/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/api/test_auth_endpoints.py.j2 +307 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/api/test_health_endpoints.py.j2 +262 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/api/test_scheduler_endpoints.py.j2 +214 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/api/test_worker_endpoints.py.j2 +165 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/cli/test_ai_rendering.py +427 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/cli/test_conversation_memory.py +465 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/components/test_scheduler.py +43 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/conftest.py.j2 +195 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/ai/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/ai/conftest.py +78 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/ai/test_health.py +157 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/ai/test_models.py +164 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/ai/test_service.py +198 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_auth_integration.py.j2 +528 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_component_integration.py.j2 +387 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_conversation_persistence.py +342 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_health_logic.py.j2 +663 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_load_test_models.py +619 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_load_test_service.py +603 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_middleware_inspector.py.j2 +248 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_scheduled_task_manager.py.j2 +292 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_system_service.py +98 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_worker_health_registration.py.j2 +257 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/test_core.py +49 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/uv.lock +1673 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/.copier-answers.yml.jinja +21 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/.dockerignore +71 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/.env.example.jinja +130 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/.gitignore +131 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/Dockerfile +53 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/Makefile.jinja +236 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/README.md.jinja +196 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/alembic/alembic.ini.jinja +111 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/alembic/env.py.jinja +91 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/alembic/script.py.mako +25 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/alembic/versions/001_initial_auth.py.jinja +51 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/__init__.py.jinja +5 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/__init__.py.jinja +6 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/ai.py.jinja +700 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/ai_rendering.py +360 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/auth.py.jinja +253 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/health.py.jinja +419 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/load_test.py.jinja +656 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/main.py.jinja +65 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/marko_terminal_renderer.py +489 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/tasks.py.jinja +328 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/__init__.py +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/__init__.py +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/ai/__init__.py +8 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/ai/router.py +329 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/auth/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/auth/router.py +64 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/deps.py +58 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/health.py.jinja +163 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/models.py.jinja +280 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/routing.py.jinja +32 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/scheduler.py.jinja +121 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/worker.py.jinja +478 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/hooks.py +144 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/main.py +31 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/middleware/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/middleware/cors.py +20 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/shutdown/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/shutdown/cleanup.py +14 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/startup/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/startup/component_health.py.jinja +418 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/startup/database_init.py.jinja +83 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/__init__.py +5 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/controls/__init__.py +27 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/controls/table.py +78 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/controls/text.py +142 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/__init__.py.jinja +47 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/ai_card.py +287 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/auth_card.py +198 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/base_card.py +256 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/card_factory.py +227 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/card_utils.py +333 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/database_card.py +420 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/fastapi_card.py +328 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/flet_card.py +267 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/redis_card.py +322 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/scheduler_card.py +352 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/services_card.py +233 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/worker_card.py +684 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/main.py.jinja +653 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/theme.py +48 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/scheduler/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/scheduler/main.py.jinja +156 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/CLAUDE.md.jinja +213 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/__init__.py +6 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/constants.py.jinja +30 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/pools.py +97 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/queues/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/queues/load_test.py +55 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/queues/media.py +49 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/queues/system.py +44 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/registry.py +139 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/tasks/__init__.py +120 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/tasks/load_tasks.py +507 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/tasks/simple_system_tasks.py +33 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/tasks/system_tasks.py +281 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/core/config.py.jinja +178 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/core/constants.py +58 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/core/db.py.jinja +176 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/core/log.py +92 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/core/security.py +62 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/entrypoints/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/entrypoints/scheduler.py.jinja +21 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/entrypoints/webserver.py +39 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/integrations/__init__.py +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/integrations/main.py +61 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/models/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/models/user.py +44 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/py.typed +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/__init__.py +8 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/config.py +130 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/conversation.py +213 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/health.py +96 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/models.py +229 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/providers.py.jinja +370 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/service.py +387 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/auth/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/auth/auth_service.py +40 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/auth/health.py +162 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/auth/user_service.py +82 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/backend/middleware_inspector.py.jinja +223 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/backend/models.py.jinja +70 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/backend/route_inspector.py.jinja +155 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/load_test.py +678 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/load_test_models.py +265 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/scheduler/__init__.py.jinja +21 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/scheduler/models.py.jinja +119 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/scheduler/scheduled_task_manager.py.jinja +273 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/scheduler/task_monitor.py.jinja +189 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/shared/__init__.py +15 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/shared/models.py +26 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/system/__init__.py +52 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/system/alerts.py +94 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/system/backup.py.jinja +119 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/system/health.py.jinja +1333 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/system/models.py +243 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/system/ui.py +52 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57223!aegis-manifesto.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57224!aegis-manifesto-dark.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57225!aegis-manifesto-square-backup.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57533!aegis-manifesto.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57534!aegis-manifesto-dark.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57538!aegis-manifesto-square-backup.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57897!aegis-manifesto.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57898!aegis-manifesto-dark.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57904!aegis-manifesto-square-backup.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!58315!aegis-manifesto.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!58316!aegis-manifesto-dark.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!58324!aegis-manifesto-square-backup.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!58837!aegis-manifesto.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!58838!aegis-manifesto-dark.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!58849!aegis-manifesto-square-backup.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/aegis-manifesto-dark.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/aegis-manifesto-square-backup.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/aegis-manifesto.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/clean-validation/.env.example.jinja +64 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/clean-validation/README.md.jinja +172 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/clean-validation/docker-compose.yml.jinja +78 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/clean-validation/mkdocs.yml.jinja +62 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/clean-validation/pyproject.toml.jinja +120 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/docker-compose.yml.jinja +200 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/docs/api.md.jinja +191 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/docs/components/scheduler.md +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/docs/components/scheduler.md.jinja +621 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/docs/development.md.jinja +215 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/docs/health.md.jinja +240 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/docs/javascripts/mermaid-config.js +62 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/docs/stylesheets/mermaid.css +95 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/mkdocs.yml.jinja +62 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/pyproject.toml.jinja +131 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/scripts/entrypoint.sh +87 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/scripts/entrypoint.sh.jinja +93 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/scripts/gen_docs.py +16 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/api/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/api/test_auth_endpoints.py.jinja +307 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/api/test_health_endpoints.py.jinja +262 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/api/test_scheduler_endpoints.py.jinja +214 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/api/test_worker_endpoints.py.jinja +165 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/cli/test_ai_rendering.py +427 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/cli/test_conversation_memory.py +465 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/components/test_scheduler.py +43 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/conftest.py.jinja +195 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/ai/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/ai/conftest.py +78 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/ai/test_health.py +157 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/ai/test_models.py +164 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/ai/test_service.py +198 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_auth_integration.py.jinja +528 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_component_integration.py.jinja +387 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_conversation_persistence.py +342 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_health_logic.py.jinja +663 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_load_test_models.py +619 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_load_test_service.py +603 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_middleware_inspector.py.jinja +248 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_scheduled_task_manager.py.jinja +292 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_system_service.py +98 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_worker_health_registration.py.jinja +257 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/test_core.py +49 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/uv.lock +1673 -0
- aegis_stack-0.2.0rc2.dist-info/METADATA +165 -0
- aegis_stack-0.2.0rc2.dist-info/RECORD +392 -0
- aegis_stack-0.2.0rc2.dist-info/WHEEL +4 -0
- aegis_stack-0.2.0rc2.dist-info/entry_points.txt +3 -0
- aegis_stack-0.2.0rc2.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Experimental Copier-based updater using git-aware template at repo root.
|
|
3
|
+
|
|
4
|
+
This module provides an alternative to the manual updater by using Copier's
|
|
5
|
+
native update mechanism with a copier.yml at the repository root.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import subprocess
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
from copier import run_update
|
|
12
|
+
from packaging.version import parse
|
|
13
|
+
from pydantic import BaseModel, Field
|
|
14
|
+
|
|
15
|
+
from aegis.core.copier_manager import load_copier_answers
|
|
16
|
+
from aegis.core.post_gen_tasks import run_post_generation_tasks
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class CopierUpdateResult(BaseModel):
|
|
20
|
+
"""Result of a Copier update operation."""
|
|
21
|
+
|
|
22
|
+
success: bool = Field(description="Whether the operation succeeded")
|
|
23
|
+
method: str = Field(default="copier-native", description="Update method used")
|
|
24
|
+
error_message: str | None = Field(
|
|
25
|
+
default=None, description="Error message if operation failed"
|
|
26
|
+
)
|
|
27
|
+
files_modified: list[str] = Field(
|
|
28
|
+
default_factory=list, description="Files that were modified"
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def get_template_root() -> Path:
|
|
33
|
+
"""
|
|
34
|
+
Get path to aegis-stack repository root (where copier.yml lives).
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
Path to aegis-stack root directory
|
|
38
|
+
"""
|
|
39
|
+
# This file is at: aegis-stack/aegis/core/copier_updater.py
|
|
40
|
+
# We want: aegis-stack/ (2 levels up)
|
|
41
|
+
return Path(__file__).parents[2]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def update_with_copier_native(
|
|
45
|
+
project_path: Path,
|
|
46
|
+
components_to_add: list[str],
|
|
47
|
+
scheduler_backend: str = "memory",
|
|
48
|
+
) -> CopierUpdateResult:
|
|
49
|
+
"""
|
|
50
|
+
EXPERIMENTAL: Use Copier's native update with git root template.
|
|
51
|
+
|
|
52
|
+
This approach uses the copier.yml at aegis-stack repo root which
|
|
53
|
+
includes _subdirectory setting. This allows Copier to recognize the
|
|
54
|
+
template as git-tracked and use proper merge conflict handling.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
project_path: Path to the existing project directory
|
|
58
|
+
components_to_add: List of component names to add
|
|
59
|
+
scheduler_backend: Backend to use for scheduler ("memory" or "sqlite")
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
CopierUpdateResult with success status
|
|
63
|
+
|
|
64
|
+
Note:
|
|
65
|
+
This is experimental. Falls back to manual updater if it fails.
|
|
66
|
+
"""
|
|
67
|
+
try:
|
|
68
|
+
# Get template root (aegis-stack/, not aegis/templates/...)
|
|
69
|
+
template_root = get_template_root()
|
|
70
|
+
|
|
71
|
+
# Build update data for Copier
|
|
72
|
+
update_data: dict[str, bool | str] = {}
|
|
73
|
+
|
|
74
|
+
for component in components_to_add:
|
|
75
|
+
include_key = f"include_{component}"
|
|
76
|
+
update_data[include_key] = True
|
|
77
|
+
|
|
78
|
+
# Add scheduler backend configuration if adding scheduler
|
|
79
|
+
if "scheduler" in components_to_add:
|
|
80
|
+
update_data["scheduler_backend"] = scheduler_backend
|
|
81
|
+
update_data["scheduler_with_persistence"] = scheduler_backend == "sqlite"
|
|
82
|
+
|
|
83
|
+
# CRITICAL: Manually update .copier-answers.yml BEFORE running copier update
|
|
84
|
+
# The `data` parameter in run_update() doesn't actually update existing answers
|
|
85
|
+
# We must edit the file directly, then Copier detects the change and regenerates
|
|
86
|
+
answers = load_copier_answers(project_path)
|
|
87
|
+
answers.update(update_data)
|
|
88
|
+
|
|
89
|
+
# Save updated answers
|
|
90
|
+
import yaml
|
|
91
|
+
|
|
92
|
+
answers_file = project_path / ".copier-answers.yml"
|
|
93
|
+
with open(answers_file, "w") as f:
|
|
94
|
+
yaml.safe_dump(answers, f, default_flow_style=False, sort_keys=False)
|
|
95
|
+
|
|
96
|
+
# Commit the updated answers (Copier requires clean repo)
|
|
97
|
+
import subprocess
|
|
98
|
+
|
|
99
|
+
try:
|
|
100
|
+
subprocess.run(
|
|
101
|
+
["git", "add", ".copier-answers.yml"],
|
|
102
|
+
cwd=project_path,
|
|
103
|
+
check=True,
|
|
104
|
+
capture_output=True,
|
|
105
|
+
)
|
|
106
|
+
subprocess.run(
|
|
107
|
+
[
|
|
108
|
+
"git",
|
|
109
|
+
"commit",
|
|
110
|
+
"-m",
|
|
111
|
+
f"Enable components: {', '.join(components_to_add)}",
|
|
112
|
+
],
|
|
113
|
+
cwd=project_path,
|
|
114
|
+
check=True,
|
|
115
|
+
capture_output=True,
|
|
116
|
+
)
|
|
117
|
+
except subprocess.CalledProcessError as e:
|
|
118
|
+
raise RuntimeError(f"Failed to commit .copier-answers.yml changes: {e}")
|
|
119
|
+
|
|
120
|
+
# Run Copier update
|
|
121
|
+
# Copier will detect the changed answers and regenerate files accordingly
|
|
122
|
+
# Copier will:
|
|
123
|
+
# 1. Use repo root as template (where .git exists)
|
|
124
|
+
# 2. _subdirectory setting in copier.yml points to actual template content
|
|
125
|
+
# 3. Detect template is git-tracked (finds .git at aegis-stack/)
|
|
126
|
+
# 4. Detect changed answers in .copier-answers.yml (we just committed them)
|
|
127
|
+
# 5. Use git diff to merge changes
|
|
128
|
+
# 6. Handle conflicts with .rej files or inline markers
|
|
129
|
+
# NOTE: _tasks removed from copier.yml - we run them ourselves below
|
|
130
|
+
run_update(
|
|
131
|
+
dst_path=str(project_path),
|
|
132
|
+
src_path=str(template_root), # Point to repo root, not subdirectory
|
|
133
|
+
defaults=True, # Use existing answers as defaults
|
|
134
|
+
overwrite=True, # Allow overwriting files
|
|
135
|
+
conflict="rej", # Create .rej files for conflicts
|
|
136
|
+
unsafe=False, # No tasks in copier.yml anymore - we run them ourselves
|
|
137
|
+
vcs_ref="HEAD", # Use latest template version
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
# CRITICAL: Copy service-specific files for newly-added services
|
|
141
|
+
# Copier can only re-render existing files - it cannot copy new directories
|
|
142
|
+
# that were excluded during initial generation
|
|
143
|
+
answers = load_copier_answers(project_path)
|
|
144
|
+
|
|
145
|
+
# Check for services in components_to_add (they start with 'include_')
|
|
146
|
+
# Service names: auth, ai
|
|
147
|
+
service_names = {"auth", "ai"}
|
|
148
|
+
newly_added_services = [
|
|
149
|
+
svc for svc in service_names if f"include_{svc}" in update_data
|
|
150
|
+
]
|
|
151
|
+
|
|
152
|
+
if newly_added_services:
|
|
153
|
+
from aegis.core.post_gen_tasks import copy_service_files
|
|
154
|
+
|
|
155
|
+
# Template content is at: template_root/aegis/templates/copier-aegis-project/
|
|
156
|
+
template_path = (
|
|
157
|
+
template_root / "aegis" / "templates" / "copier-aegis-project"
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
for service_name in newly_added_services:
|
|
161
|
+
copy_service_files(project_path, service_name, template_path)
|
|
162
|
+
|
|
163
|
+
# Run post-generation tasks with explicit working directory control
|
|
164
|
+
# This ensures consistent behavior with initial generation
|
|
165
|
+
include_auth = answers.get("include_auth", False)
|
|
166
|
+
|
|
167
|
+
# Run shared post-generation tasks
|
|
168
|
+
run_post_generation_tasks(project_path, include_auth=include_auth)
|
|
169
|
+
|
|
170
|
+
return CopierUpdateResult(
|
|
171
|
+
success=True,
|
|
172
|
+
method="copier-native",
|
|
173
|
+
files_modified=[], # Copier doesn't provide this info easily
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
except Exception as e:
|
|
177
|
+
return CopierUpdateResult(
|
|
178
|
+
success=False, method="copier-native", error_message=str(e)
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
# Version Management Functions
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def get_current_template_commit(project_path: Path) -> str | None:
|
|
186
|
+
"""
|
|
187
|
+
Get the git commit hash of the template used to generate the project.
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
project_path: Path to the project directory
|
|
191
|
+
|
|
192
|
+
Returns:
|
|
193
|
+
Commit hash string or None if not found
|
|
194
|
+
"""
|
|
195
|
+
try:
|
|
196
|
+
answers = load_copier_answers(project_path)
|
|
197
|
+
commit_hash = answers.get("_commit")
|
|
198
|
+
if commit_hash and commit_hash != "None":
|
|
199
|
+
return commit_hash
|
|
200
|
+
return None
|
|
201
|
+
except Exception:
|
|
202
|
+
return None
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def get_available_versions(template_root: Path | None = None) -> list[str]:
|
|
206
|
+
"""
|
|
207
|
+
Get list of available template versions from git tags.
|
|
208
|
+
|
|
209
|
+
Args:
|
|
210
|
+
template_root: Path to template repository (default: auto-detect)
|
|
211
|
+
|
|
212
|
+
Returns:
|
|
213
|
+
List of version strings sorted by PEP 440 (newest first)
|
|
214
|
+
"""
|
|
215
|
+
if template_root is None:
|
|
216
|
+
template_root = get_template_root()
|
|
217
|
+
|
|
218
|
+
try:
|
|
219
|
+
result = subprocess.run(
|
|
220
|
+
["git", "tag", "--list", "v*"],
|
|
221
|
+
cwd=template_root,
|
|
222
|
+
capture_output=True,
|
|
223
|
+
text=True,
|
|
224
|
+
check=True,
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
# Parse tags (remove 'v' prefix and filter valid versions)
|
|
228
|
+
versions = []
|
|
229
|
+
for tag in result.stdout.strip().split("\n"):
|
|
230
|
+
if tag.startswith("v"):
|
|
231
|
+
version_str = tag[1:] # Remove 'v' prefix
|
|
232
|
+
try:
|
|
233
|
+
parse(version_str) # Validate version
|
|
234
|
+
versions.append(version_str)
|
|
235
|
+
except Exception:
|
|
236
|
+
continue
|
|
237
|
+
|
|
238
|
+
# Sort by PEP 440 (newest first)
|
|
239
|
+
versions.sort(key=lambda v: parse(v), reverse=True)
|
|
240
|
+
return versions
|
|
241
|
+
|
|
242
|
+
except Exception:
|
|
243
|
+
return []
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def get_latest_version(template_root: Path | None = None) -> str | None:
|
|
247
|
+
"""
|
|
248
|
+
Get the latest template version from git tags.
|
|
249
|
+
|
|
250
|
+
Args:
|
|
251
|
+
template_root: Path to template repository (default: auto-detect)
|
|
252
|
+
|
|
253
|
+
Returns:
|
|
254
|
+
Latest version string or None if no versions found
|
|
255
|
+
"""
|
|
256
|
+
versions = get_available_versions(template_root)
|
|
257
|
+
return versions[0] if versions else None
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def resolve_ref_to_commit(ref: str, repo_path: Path) -> str | None:
|
|
261
|
+
"""
|
|
262
|
+
Resolve a git reference (HEAD, branch, tag, commit) to full commit SHA.
|
|
263
|
+
|
|
264
|
+
Args:
|
|
265
|
+
ref: Git reference (HEAD, branch name, tag, or commit hash)
|
|
266
|
+
repo_path: Path to git repository
|
|
267
|
+
|
|
268
|
+
Returns:
|
|
269
|
+
Full commit SHA or None if resolution fails
|
|
270
|
+
"""
|
|
271
|
+
try:
|
|
272
|
+
result = subprocess.run(
|
|
273
|
+
["git", "rev-parse", ref],
|
|
274
|
+
cwd=repo_path,
|
|
275
|
+
capture_output=True,
|
|
276
|
+
text=True,
|
|
277
|
+
check=True,
|
|
278
|
+
)
|
|
279
|
+
return result.stdout.strip()
|
|
280
|
+
except subprocess.CalledProcessError:
|
|
281
|
+
return None
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def resolve_version_to_ref(
|
|
285
|
+
version: str | None, template_root: Path | None = None
|
|
286
|
+
) -> str:
|
|
287
|
+
"""
|
|
288
|
+
Resolve a version string to a git reference.
|
|
289
|
+
|
|
290
|
+
Args:
|
|
291
|
+
version: Version string ("latest", "0.2.0", commit hash, or None)
|
|
292
|
+
template_root: Path to template repository (default: auto-detect)
|
|
293
|
+
|
|
294
|
+
Returns:
|
|
295
|
+
Git reference string (tag name, commit hash, or "HEAD")
|
|
296
|
+
"""
|
|
297
|
+
if template_root is None:
|
|
298
|
+
template_root = get_template_root()
|
|
299
|
+
|
|
300
|
+
# None or "latest" -> use latest version tag
|
|
301
|
+
if not version or version == "latest":
|
|
302
|
+
latest = get_latest_version(template_root)
|
|
303
|
+
if latest:
|
|
304
|
+
return f"v{latest}"
|
|
305
|
+
return "HEAD"
|
|
306
|
+
|
|
307
|
+
# Check if it's a commit hash (40 hex characters)
|
|
308
|
+
if len(version) == 40 and all(c in "0123456789abcdef" for c in version.lower()):
|
|
309
|
+
return version
|
|
310
|
+
|
|
311
|
+
# Check if it looks like a version number (add 'v' prefix)
|
|
312
|
+
try:
|
|
313
|
+
parse(version)
|
|
314
|
+
# Valid version number - check if tag exists
|
|
315
|
+
tag_name = f"v{version}"
|
|
316
|
+
result = subprocess.run(
|
|
317
|
+
["git", "rev-parse", "--verify", f"refs/tags/{tag_name}"],
|
|
318
|
+
cwd=template_root,
|
|
319
|
+
capture_output=True,
|
|
320
|
+
check=False,
|
|
321
|
+
)
|
|
322
|
+
if result.returncode == 0:
|
|
323
|
+
return tag_name
|
|
324
|
+
except Exception:
|
|
325
|
+
pass
|
|
326
|
+
|
|
327
|
+
# Fall back to using the version string as-is (might be a branch name)
|
|
328
|
+
return version
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
def validate_clean_git_tree(project_path: Path) -> tuple[bool, str]:
|
|
332
|
+
"""
|
|
333
|
+
Check if the project's git working tree is clean.
|
|
334
|
+
|
|
335
|
+
Args:
|
|
336
|
+
project_path: Path to project directory
|
|
337
|
+
|
|
338
|
+
Returns:
|
|
339
|
+
Tuple of (is_clean: bool, message: str)
|
|
340
|
+
"""
|
|
341
|
+
try:
|
|
342
|
+
result = subprocess.run(
|
|
343
|
+
["git", "status", "--porcelain"],
|
|
344
|
+
cwd=project_path,
|
|
345
|
+
capture_output=True,
|
|
346
|
+
text=True,
|
|
347
|
+
check=True,
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
if result.stdout.strip():
|
|
351
|
+
return False, "Git tree has uncommitted changes"
|
|
352
|
+
|
|
353
|
+
return True, "Git tree is clean"
|
|
354
|
+
|
|
355
|
+
except subprocess.CalledProcessError as e:
|
|
356
|
+
return False, f"Failed to check git status: {e}"
|
|
357
|
+
except Exception as e:
|
|
358
|
+
return False, f"Error checking git status: {e}"
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
def get_changelog(from_ref: str, to_ref: str, template_root: Path | None = None) -> str:
|
|
362
|
+
"""
|
|
363
|
+
Get changelog between two git references.
|
|
364
|
+
|
|
365
|
+
Args:
|
|
366
|
+
from_ref: Starting git reference (commit, tag, or branch)
|
|
367
|
+
to_ref: Ending git reference
|
|
368
|
+
template_root: Path to template repository (default: auto-detect)
|
|
369
|
+
|
|
370
|
+
Returns:
|
|
371
|
+
Formatted changelog string
|
|
372
|
+
"""
|
|
373
|
+
if template_root is None:
|
|
374
|
+
template_root = get_template_root()
|
|
375
|
+
|
|
376
|
+
try:
|
|
377
|
+
# Get commit log between refs
|
|
378
|
+
result = subprocess.run(
|
|
379
|
+
[
|
|
380
|
+
"git",
|
|
381
|
+
"log",
|
|
382
|
+
"--oneline",
|
|
383
|
+
"--no-merges",
|
|
384
|
+
"--pretty=format:%s",
|
|
385
|
+
f"{from_ref}..{to_ref}",
|
|
386
|
+
],
|
|
387
|
+
cwd=template_root,
|
|
388
|
+
capture_output=True,
|
|
389
|
+
text=True,
|
|
390
|
+
check=True,
|
|
391
|
+
)
|
|
392
|
+
|
|
393
|
+
if not result.stdout.strip():
|
|
394
|
+
return "No changes"
|
|
395
|
+
|
|
396
|
+
# Parse commits and categorize
|
|
397
|
+
commits = result.stdout.strip().split("\n")
|
|
398
|
+
features = []
|
|
399
|
+
fixes = []
|
|
400
|
+
breaking = []
|
|
401
|
+
other = []
|
|
402
|
+
|
|
403
|
+
for commit in commits:
|
|
404
|
+
commit_lower = commit.lower()
|
|
405
|
+
if "breaking:" in commit_lower or "breaking change" in commit_lower:
|
|
406
|
+
breaking.append(commit)
|
|
407
|
+
elif commit.startswith("feat:") or commit.startswith("feature:"):
|
|
408
|
+
features.append(commit)
|
|
409
|
+
elif commit.startswith("fix:"):
|
|
410
|
+
fixes.append(commit)
|
|
411
|
+
else:
|
|
412
|
+
other.append(commit)
|
|
413
|
+
|
|
414
|
+
# Format changelog
|
|
415
|
+
lines = []
|
|
416
|
+
|
|
417
|
+
if breaking:
|
|
418
|
+
lines.append("⚠️ Breaking Changes:")
|
|
419
|
+
for commit in breaking:
|
|
420
|
+
lines.append(f" • {commit}")
|
|
421
|
+
lines.append("")
|
|
422
|
+
|
|
423
|
+
if features:
|
|
424
|
+
lines.append("✨ New Features:")
|
|
425
|
+
for commit in features:
|
|
426
|
+
lines.append(f" • {commit}")
|
|
427
|
+
lines.append("")
|
|
428
|
+
|
|
429
|
+
if fixes:
|
|
430
|
+
lines.append("🐛 Bug Fixes:")
|
|
431
|
+
for commit in fixes:
|
|
432
|
+
lines.append(f" • {commit}")
|
|
433
|
+
lines.append("")
|
|
434
|
+
|
|
435
|
+
if other:
|
|
436
|
+
lines.append("📝 Other Changes:")
|
|
437
|
+
for commit in other:
|
|
438
|
+
lines.append(f" • {commit}")
|
|
439
|
+
|
|
440
|
+
return "\n".join(lines).strip()
|
|
441
|
+
|
|
442
|
+
except Exception as e:
|
|
443
|
+
return f"Error generating changelog: {e}"
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
def get_commit_for_version(
|
|
447
|
+
version: str, template_root: Path | None = None
|
|
448
|
+
) -> str | None:
|
|
449
|
+
"""
|
|
450
|
+
Get the git commit hash for a version tag.
|
|
451
|
+
|
|
452
|
+
Args:
|
|
453
|
+
version: Version string (with or without 'v' prefix)
|
|
454
|
+
template_root: Path to template repository (default: auto-detect)
|
|
455
|
+
|
|
456
|
+
Returns:
|
|
457
|
+
Commit hash or None if tag not found
|
|
458
|
+
"""
|
|
459
|
+
if template_root is None:
|
|
460
|
+
template_root = get_template_root()
|
|
461
|
+
|
|
462
|
+
# Ensure version has 'v' prefix
|
|
463
|
+
tag_name = version if version.startswith("v") else f"v{version}"
|
|
464
|
+
|
|
465
|
+
try:
|
|
466
|
+
result = subprocess.run(
|
|
467
|
+
["git", "rev-list", "-n", "1", tag_name],
|
|
468
|
+
cwd=template_root,
|
|
469
|
+
capture_output=True,
|
|
470
|
+
text=True,
|
|
471
|
+
check=True,
|
|
472
|
+
)
|
|
473
|
+
return result.stdout.strip()
|
|
474
|
+
except Exception:
|
|
475
|
+
return None
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Component dependency resolution for Aegis Stack.
|
|
3
|
+
|
|
4
|
+
This module handles dependency resolution, validation, and recommendations
|
|
5
|
+
for component selection during project generation.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .components import COMPONENTS
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class DependencyResolver:
|
|
12
|
+
"""Handles component dependency resolution and validation."""
|
|
13
|
+
|
|
14
|
+
@staticmethod
|
|
15
|
+
def resolve_dependencies(selected_components: list[str]) -> list[str]:
|
|
16
|
+
"""
|
|
17
|
+
Resolve all dependencies and return final component list.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
selected_components: List of component names selected by user
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
Complete list of components including dependencies
|
|
24
|
+
|
|
25
|
+
Raises:
|
|
26
|
+
ValueError: If any selected components are invalid
|
|
27
|
+
"""
|
|
28
|
+
# Validate all components first
|
|
29
|
+
errors = DependencyResolver.validate_components(selected_components)
|
|
30
|
+
if errors:
|
|
31
|
+
raise ValueError(f"Invalid components: {'; '.join(errors)}")
|
|
32
|
+
|
|
33
|
+
resolved = set(selected_components)
|
|
34
|
+
|
|
35
|
+
# Resolve hard dependencies recursively
|
|
36
|
+
while True:
|
|
37
|
+
before_size = len(resolved)
|
|
38
|
+
for component_name in list(resolved):
|
|
39
|
+
if component_name not in COMPONENTS:
|
|
40
|
+
continue
|
|
41
|
+
|
|
42
|
+
component = COMPONENTS[component_name]
|
|
43
|
+
if component.requires:
|
|
44
|
+
resolved.update(component.requires)
|
|
45
|
+
|
|
46
|
+
if len(resolved) == before_size:
|
|
47
|
+
break # No new dependencies added
|
|
48
|
+
|
|
49
|
+
return sorted(resolved)
|
|
50
|
+
|
|
51
|
+
@staticmethod
|
|
52
|
+
def validate_components(components: list[str]) -> list[str]:
|
|
53
|
+
"""
|
|
54
|
+
Validate component selection and return errors.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
components: List of component names to validate
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
List of error messages (empty if valid)
|
|
61
|
+
"""
|
|
62
|
+
errors = []
|
|
63
|
+
|
|
64
|
+
for component in components:
|
|
65
|
+
if component not in COMPONENTS:
|
|
66
|
+
errors.append(f"Unknown component: {component}")
|
|
67
|
+
continue
|
|
68
|
+
|
|
69
|
+
spec = COMPONENTS[component]
|
|
70
|
+
|
|
71
|
+
# Check conflicts
|
|
72
|
+
if spec.conflicts:
|
|
73
|
+
for conflict in spec.conflicts:
|
|
74
|
+
if conflict in components:
|
|
75
|
+
errors.append(
|
|
76
|
+
f"Component '{component}' conflicts with '{conflict}'"
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
return errors
|
|
80
|
+
|
|
81
|
+
@staticmethod
|
|
82
|
+
def get_recommendations(selected_components: list[str]) -> list[str]:
|
|
83
|
+
"""
|
|
84
|
+
Get recommended components based on selection.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
selected_components: List of already selected components
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
List of recommended component names not already selected
|
|
91
|
+
"""
|
|
92
|
+
recommendations = set()
|
|
93
|
+
|
|
94
|
+
for component_name in selected_components:
|
|
95
|
+
if component_name not in COMPONENTS:
|
|
96
|
+
continue
|
|
97
|
+
|
|
98
|
+
component = COMPONENTS[component_name]
|
|
99
|
+
if component.recommends:
|
|
100
|
+
for rec in component.recommends:
|
|
101
|
+
if rec not in selected_components:
|
|
102
|
+
recommendations.add(rec)
|
|
103
|
+
|
|
104
|
+
return sorted(recommendations)
|
|
105
|
+
|
|
106
|
+
@staticmethod
|
|
107
|
+
def get_missing_dependencies(selected_components: list[str]) -> list[str]:
|
|
108
|
+
"""
|
|
109
|
+
Get dependencies that would be auto-added.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
selected_components: List of user-selected components
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
List of dependencies that would be automatically added
|
|
116
|
+
"""
|
|
117
|
+
resolved = DependencyResolver.resolve_dependencies(selected_components)
|
|
118
|
+
auto_added = set(resolved) - set(selected_components)
|
|
119
|
+
return sorted(auto_added)
|