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
aegis/core/CLAUDE.md
ADDED
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
# CLI Core Development Guide
|
|
2
|
+
|
|
3
|
+
This guide covers CLI development patterns for Aegis Stack's core command system.
|
|
4
|
+
|
|
5
|
+
## Component System Architecture
|
|
6
|
+
|
|
7
|
+
### Component Registry (`components.py`)
|
|
8
|
+
The component system uses a centralized registry with typed component specifications:
|
|
9
|
+
|
|
10
|
+
```python
|
|
11
|
+
@dataclass
|
|
12
|
+
class ComponentSpec:
|
|
13
|
+
"""Specification for a component."""
|
|
14
|
+
name: str
|
|
15
|
+
description: str
|
|
16
|
+
type: ComponentType
|
|
17
|
+
requires: list[str] = field(default_factory=list)
|
|
18
|
+
recommends: list[str] = field(default_factory=list)
|
|
19
|
+
conflicts: list[str] = field(default_factory=list)
|
|
20
|
+
|
|
21
|
+
# Component registry
|
|
22
|
+
COMPONENTS: dict[str, ComponentSpec] = {
|
|
23
|
+
"redis": ComponentSpec(
|
|
24
|
+
name="redis",
|
|
25
|
+
description="Redis server for caching and message queues",
|
|
26
|
+
type=ComponentType.INFRASTRUCTURE,
|
|
27
|
+
),
|
|
28
|
+
"worker": ComponentSpec(
|
|
29
|
+
name="worker",
|
|
30
|
+
description="Background task processing with arq",
|
|
31
|
+
type=ComponentType.INFRASTRUCTURE,
|
|
32
|
+
requires=["redis"],
|
|
33
|
+
),
|
|
34
|
+
"scheduler": ComponentSpec(
|
|
35
|
+
name="scheduler",
|
|
36
|
+
description="Scheduled task execution with APScheduler",
|
|
37
|
+
type=ComponentType.INFRASTRUCTURE,
|
|
38
|
+
),
|
|
39
|
+
"database": ComponentSpec(
|
|
40
|
+
name="database",
|
|
41
|
+
description="SQLite database with SQLModel ORM",
|
|
42
|
+
type=ComponentType.INFRASTRUCTURE,
|
|
43
|
+
requires=[], # Standalone component
|
|
44
|
+
),
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Component Types
|
|
49
|
+
```python
|
|
50
|
+
class ComponentType(Enum):
|
|
51
|
+
"""Component types for organization."""
|
|
52
|
+
INFRASTRUCTURE = "infrastructure" # Redis, workers, databases
|
|
53
|
+
SERVICE = "service" # Future: API services, microservices
|
|
54
|
+
INTEGRATION = "integration" # Future: External integrations
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Dependency Resolution Patterns
|
|
58
|
+
|
|
59
|
+
### Dependency Resolver (`dependency_resolver.py`)
|
|
60
|
+
The dependency resolver handles component relationships and validation:
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
class DependencyResolver:
|
|
64
|
+
"""Resolves component dependencies and validates selections."""
|
|
65
|
+
|
|
66
|
+
@staticmethod
|
|
67
|
+
def validate_components(components: list[str]) -> list[str]:
|
|
68
|
+
"""Validate component names and return errors."""
|
|
69
|
+
errors = []
|
|
70
|
+
for component in components:
|
|
71
|
+
if component not in COMPONENTS:
|
|
72
|
+
available = list(COMPONENTS.keys())
|
|
73
|
+
# Suggest similar components
|
|
74
|
+
suggestion = DependencyResolver._suggest_component(component, available)
|
|
75
|
+
if suggestion:
|
|
76
|
+
errors.append(f"Unknown component '{component}'. Did you mean '{suggestion}'?")
|
|
77
|
+
else:
|
|
78
|
+
errors.append(f"Unknown component '{component}'. Available: {', '.join(available)}")
|
|
79
|
+
return errors
|
|
80
|
+
|
|
81
|
+
@staticmethod
|
|
82
|
+
def resolve_dependencies(selected: list[str]) -> list[str]:
|
|
83
|
+
"""Resolve all dependencies for selected components."""
|
|
84
|
+
resolved = set(selected)
|
|
85
|
+
|
|
86
|
+
# Add required dependencies
|
|
87
|
+
queue = list(selected)
|
|
88
|
+
while queue:
|
|
89
|
+
component = queue.pop(0)
|
|
90
|
+
if component in COMPONENTS:
|
|
91
|
+
for required in COMPONENTS[component].requires:
|
|
92
|
+
if required not in resolved:
|
|
93
|
+
resolved.add(required)
|
|
94
|
+
queue.append(required)
|
|
95
|
+
|
|
96
|
+
return sorted(resolved)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Validation Patterns
|
|
100
|
+
```python
|
|
101
|
+
def validate_project_name(project_name: str) -> None:
|
|
102
|
+
"""Validate project name and raise typer.Exit if invalid."""
|
|
103
|
+
import re
|
|
104
|
+
|
|
105
|
+
# Check for invalid characters
|
|
106
|
+
if not re.match(r"^[a-zA-Z0-9_-]+$", project_name):
|
|
107
|
+
typer.echo("❌ Invalid project name. Only letters, numbers, hyphens, and underscores are allowed.", err=True)
|
|
108
|
+
raise typer.Exit(1)
|
|
109
|
+
|
|
110
|
+
# Check for reserved names
|
|
111
|
+
reserved_names = {"aegis", "aegis-stack"}
|
|
112
|
+
if project_name.lower() in reserved_names:
|
|
113
|
+
typer.echo(f"❌ '{project_name}' is a reserved name.", err=True)
|
|
114
|
+
raise typer.Exit(1)
|
|
115
|
+
|
|
116
|
+
# Check length limit
|
|
117
|
+
if len(project_name) > 50:
|
|
118
|
+
typer.echo("❌ Project name too long. Maximum 50 characters allowed.", err=True)
|
|
119
|
+
raise typer.Exit(1)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Template Generation Logic
|
|
123
|
+
|
|
124
|
+
### Template Generator (`template_generator.py`)
|
|
125
|
+
The template generator converts component selections into cookiecutter context:
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
class TemplateGenerator:
|
|
129
|
+
"""Generates template context from component selections."""
|
|
130
|
+
|
|
131
|
+
def __init__(self, project_name: str, components: list[str]):
|
|
132
|
+
self.project_name = project_name
|
|
133
|
+
self.project_slug = self._generate_slug(project_name)
|
|
134
|
+
self.components = components
|
|
135
|
+
|
|
136
|
+
def get_template_context(self) -> dict[str, Any]:
|
|
137
|
+
"""Generate cookiecutter context from component selections."""
|
|
138
|
+
return {
|
|
139
|
+
"project_name": self.project_name,
|
|
140
|
+
"project_slug": self.project_slug,
|
|
141
|
+
"project_description": f"A production-ready Python application",
|
|
142
|
+
"author_name": "Developer",
|
|
143
|
+
"author_email": "dev@example.com",
|
|
144
|
+
"version": "0.1.0",
|
|
145
|
+
"python_version": "3.11",
|
|
146
|
+
"include_scheduler": "yes" if "scheduler" in self.components else "no",
|
|
147
|
+
"include_worker": "yes" if "worker" in self.components else "no",
|
|
148
|
+
"include_database": "yes" if "database" in self.components else "no",
|
|
149
|
+
"include_cache": "yes" if "cache" in self.components else "no",
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
def get_template_files(self) -> list[str]:
|
|
153
|
+
"""Get list of template files that will be generated."""
|
|
154
|
+
files = []
|
|
155
|
+
|
|
156
|
+
# Core files (always included)
|
|
157
|
+
files.extend([
|
|
158
|
+
"app/components/backend/main.py",
|
|
159
|
+
"app/components/frontend/main.py",
|
|
160
|
+
"app/core/config.py",
|
|
161
|
+
"app/integrations/main.py",
|
|
162
|
+
])
|
|
163
|
+
|
|
164
|
+
# Component-specific files
|
|
165
|
+
if "scheduler" in self.components:
|
|
166
|
+
files.extend([
|
|
167
|
+
"app/components/scheduler/main.py",
|
|
168
|
+
"app/entrypoints/scheduler.py",
|
|
169
|
+
"tests/components/test_scheduler.py",
|
|
170
|
+
])
|
|
171
|
+
|
|
172
|
+
if "worker" in self.components:
|
|
173
|
+
files.extend([
|
|
174
|
+
"app/components/worker/queues/system.py",
|
|
175
|
+
"app/components/worker/tasks/system_tasks.py",
|
|
176
|
+
"app/services/load_test.py",
|
|
177
|
+
"tests/services/test_worker_health_registration.py",
|
|
178
|
+
])
|
|
179
|
+
|
|
180
|
+
if "database" in self.components:
|
|
181
|
+
files.extend([
|
|
182
|
+
"app/core/db.py",
|
|
183
|
+
"tests/conftest.py", # Database testing fixtures
|
|
184
|
+
])
|
|
185
|
+
|
|
186
|
+
return sorted(files)
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## CLI Command Structure
|
|
190
|
+
|
|
191
|
+
### Command Definitions (`__main__.py`)
|
|
192
|
+
Commands use Typer with proper validation and error handling:
|
|
193
|
+
|
|
194
|
+
```python
|
|
195
|
+
@app.command()
|
|
196
|
+
def init(
|
|
197
|
+
project_name: str = typer.Argument(..., help="Name of the new Aegis Stack project to create"),
|
|
198
|
+
components: str | None = typer.Option(
|
|
199
|
+
None,
|
|
200
|
+
"--components", "-c",
|
|
201
|
+
callback=validate_and_resolve_components,
|
|
202
|
+
help="Comma-separated list of components (redis,worker,scheduler)",
|
|
203
|
+
),
|
|
204
|
+
interactive: bool = typer.Option(
|
|
205
|
+
True, "--interactive/--no-interactive", "-i/-ni",
|
|
206
|
+
help="Use interactive component selection"
|
|
207
|
+
),
|
|
208
|
+
force: bool = typer.Option(False, "--force", "-f", help="Overwrite existing directory if it exists"),
|
|
209
|
+
output_dir: str | None = typer.Option(None, "--output-dir", "-o", help="Directory to create the project in"),
|
|
210
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Skip confirmation prompt"),
|
|
211
|
+
) -> None:
|
|
212
|
+
"""Initialize a new Aegis Stack project with battle-tested component combinations."""
|
|
213
|
+
|
|
214
|
+
# Validate project name first
|
|
215
|
+
validate_project_name(project_name)
|
|
216
|
+
|
|
217
|
+
# Show project configuration
|
|
218
|
+
show_project_configuration(project_name, components, output_dir)
|
|
219
|
+
|
|
220
|
+
# Confirm before proceeding
|
|
221
|
+
if not yes and not typer.confirm("🚀 Create this project?"):
|
|
222
|
+
typer.echo("❌ Project creation cancelled")
|
|
223
|
+
raise typer.Exit(0)
|
|
224
|
+
|
|
225
|
+
# Create project using cookiecutter
|
|
226
|
+
create_project_with_cookiecutter(project_name, components, output_dir, force)
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Interactive Selection Patterns
|
|
230
|
+
```python
|
|
231
|
+
def interactive_component_selection() -> list[str]:
|
|
232
|
+
"""Interactive component selection with dependency awareness."""
|
|
233
|
+
|
|
234
|
+
typer.echo("🎯 Component Selection")
|
|
235
|
+
typer.echo("=" * 40)
|
|
236
|
+
typer.echo("✅ Core components (backend + frontend) included automatically\\n")
|
|
237
|
+
|
|
238
|
+
selected = []
|
|
239
|
+
|
|
240
|
+
# Infrastructure components
|
|
241
|
+
typer.echo("🏗️ Infrastructure Components:")
|
|
242
|
+
if typer.confirm(" Add Redis (caching, message queues)?"):
|
|
243
|
+
selected.append("redis")
|
|
244
|
+
|
|
245
|
+
if "redis" in selected:
|
|
246
|
+
if typer.confirm(" Add worker infrastructure (background tasks)?"):
|
|
247
|
+
selected.append("worker")
|
|
248
|
+
else:
|
|
249
|
+
if typer.confirm(" Add worker infrastructure? (will auto-add Redis)"):
|
|
250
|
+
selected.extend(["redis", "worker"])
|
|
251
|
+
|
|
252
|
+
if typer.confirm(" Add scheduler infrastructure (scheduled tasks)?"):
|
|
253
|
+
selected.append("scheduler")
|
|
254
|
+
|
|
255
|
+
return selected
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
## CLI Error Handling Patterns
|
|
259
|
+
|
|
260
|
+
### Validation Callbacks
|
|
261
|
+
```python
|
|
262
|
+
def validate_and_resolve_components(
|
|
263
|
+
ctx: typer.Context, param: typer.CallbackParam, value: str | None
|
|
264
|
+
) -> list[str] | None:
|
|
265
|
+
"""Validate and resolve component dependencies."""
|
|
266
|
+
if not value:
|
|
267
|
+
return None
|
|
268
|
+
|
|
269
|
+
# Parse comma-separated string
|
|
270
|
+
components_raw = [c.strip() for c in value.split(",")]
|
|
271
|
+
|
|
272
|
+
# Check for empty components
|
|
273
|
+
if any(not c for c in components_raw):
|
|
274
|
+
typer.echo("❌ Empty component name is not allowed", err=True)
|
|
275
|
+
raise typer.Exit(1)
|
|
276
|
+
|
|
277
|
+
selected = [c for c in components_raw if c]
|
|
278
|
+
|
|
279
|
+
# Validate components exist
|
|
280
|
+
errors = DependencyResolver.validate_components(selected)
|
|
281
|
+
if errors:
|
|
282
|
+
for error in errors:
|
|
283
|
+
typer.echo(f"❌ {error}", err=True)
|
|
284
|
+
raise typer.Exit(1)
|
|
285
|
+
|
|
286
|
+
# Resolve dependencies
|
|
287
|
+
resolved = DependencyResolver.resolve_dependencies(selected)
|
|
288
|
+
|
|
289
|
+
# Show dependency resolution
|
|
290
|
+
auto_added = DependencyResolver.get_missing_dependencies(selected)
|
|
291
|
+
if auto_added:
|
|
292
|
+
typer.echo(f"📦 Auto-added dependencies: {', '.join(auto_added)}")
|
|
293
|
+
|
|
294
|
+
return resolved
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### Error Message Quality
|
|
298
|
+
```python
|
|
299
|
+
def show_helpful_error(component: str, available: list[str]) -> None:
|
|
300
|
+
"""Show helpful error message with suggestions."""
|
|
301
|
+
# Suggest similar components using fuzzy matching
|
|
302
|
+
suggestion = find_closest_match(component, available)
|
|
303
|
+
|
|
304
|
+
if suggestion:
|
|
305
|
+
typer.echo(f"❌ Unknown component '{component}'. Did you mean '{suggestion}'?", err=True)
|
|
306
|
+
else:
|
|
307
|
+
typer.echo(f"❌ Unknown component '{component}'.", err=True)
|
|
308
|
+
|
|
309
|
+
typer.echo(f" Available components: {', '.join(available)}", err=True)
|
|
310
|
+
typer.echo(" Use 'aegis components' to see detailed information", err=True)
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
## CLI Development Best Practices
|
|
314
|
+
|
|
315
|
+
### Command Design
|
|
316
|
+
1. **Clear naming** - Use descriptive command and option names
|
|
317
|
+
2. **Helpful descriptions** - Provide clear help text for all commands
|
|
318
|
+
3. **Sensible defaults** - Choose good default values for options
|
|
319
|
+
4. **Progressive disclosure** - Show basic options first, advanced options with help
|
|
320
|
+
5. **Consistent patterns** - Use similar patterns across commands
|
|
321
|
+
|
|
322
|
+
### Validation Strategy
|
|
323
|
+
1. **Early validation** - Validate inputs as early as possible
|
|
324
|
+
2. **Clear error messages** - Provide actionable error messages
|
|
325
|
+
3. **Helpful suggestions** - Suggest corrections when possible
|
|
326
|
+
4. **Context-aware errors** - Show relevant information in errors
|
|
327
|
+
5. **Graceful degradation** - Handle edge cases gracefully
|
|
328
|
+
|
|
329
|
+
### User Experience
|
|
330
|
+
1. **Interactive guidance** - Provide interactive help when possible
|
|
331
|
+
2. **Visual feedback** - Use emojis and formatting for clarity
|
|
332
|
+
3. **Progress indication** - Show progress for long-running operations
|
|
333
|
+
4. **Confirmation prompts** - Ask before destructive operations
|
|
334
|
+
5. **Escape hatches** - Provide ways to cancel or undo operations
|
|
335
|
+
|
|
336
|
+
### Code Organization
|
|
337
|
+
1. **Separation of concerns** - Keep CLI logic separate from business logic
|
|
338
|
+
2. **Reusable functions** - Extract common patterns into functions
|
|
339
|
+
3. **Type safety** - Use proper type hints throughout
|
|
340
|
+
4. **Error handling** - Handle all possible error conditions
|
|
341
|
+
5. **Testing support** - Design code to be easily testable
|
|
342
|
+
|
|
343
|
+
## CLI Testing Patterns
|
|
344
|
+
|
|
345
|
+
### Command Testing
|
|
346
|
+
```python
|
|
347
|
+
def test_component_validation():
|
|
348
|
+
"""Test component validation logic."""
|
|
349
|
+
# Valid components
|
|
350
|
+
assert validate_components(["worker", "scheduler"]) == []
|
|
351
|
+
|
|
352
|
+
# Invalid components
|
|
353
|
+
errors = validate_components(["invalid"])
|
|
354
|
+
assert "Unknown component 'invalid'" in errors[0]
|
|
355
|
+
|
|
356
|
+
# Suggestions
|
|
357
|
+
errors = validate_components(["schedul"])
|
|
358
|
+
assert "Did you mean 'scheduler'?" in errors[0]
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
### Integration Testing
|
|
362
|
+
```python
|
|
363
|
+
def test_project_generation():
|
|
364
|
+
"""Test full project generation workflow."""
|
|
365
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
366
|
+
result = run_aegis_init(
|
|
367
|
+
"test-project",
|
|
368
|
+
["worker"],
|
|
369
|
+
Path(temp_dir)
|
|
370
|
+
)
|
|
371
|
+
|
|
372
|
+
assert result.success
|
|
373
|
+
assert (result.project_path / "app" / "components" / "worker").exists()
|
|
374
|
+
assert (result.project_path / "docker-compose.yml").exists()
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
This approach ensures the CLI is maintainable, user-friendly, and follows established patterns for command-line tool development.
|
aegis/core/__init__.py
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Component file tracking infrastructure.
|
|
3
|
+
|
|
4
|
+
This module provides functionality to identify which files belong to which
|
|
5
|
+
components by parsing the Copier template's exclusion rules.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import re
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
import yaml
|
|
13
|
+
|
|
14
|
+
# Constants
|
|
15
|
+
PROJECT_SLUG_PLACEHOLDER = "{{ project_slug }}"
|
|
16
|
+
JINJA_EXTENSION = ".jinja"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def get_template_path() -> Path:
|
|
20
|
+
"""Get path to Copier template directory."""
|
|
21
|
+
return Path(__file__).parent.parent / "templates" / "copier-aegis-project"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def load_copier_config() -> dict[str, Any]:
|
|
25
|
+
"""
|
|
26
|
+
Load copier.yml configuration.
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
Dictionary containing Copier template configuration
|
|
30
|
+
|
|
31
|
+
Raises:
|
|
32
|
+
FileNotFoundError: If copier.yml doesn't exist
|
|
33
|
+
yaml.YAMLError: If copier.yml is invalid
|
|
34
|
+
"""
|
|
35
|
+
# copier.yml is now at repo root (aegis-stack/copier.yml)
|
|
36
|
+
# not in the template subdirectory
|
|
37
|
+
repo_root = Path(__file__).parent.parent.parent
|
|
38
|
+
copier_yml = repo_root / "copier.yml"
|
|
39
|
+
|
|
40
|
+
if not copier_yml.exists():
|
|
41
|
+
raise FileNotFoundError(f"copier.yml not found at {copier_yml}")
|
|
42
|
+
|
|
43
|
+
try:
|
|
44
|
+
with open(copier_yml) as f:
|
|
45
|
+
return yaml.safe_load(f) or {}
|
|
46
|
+
except yaml.YAMLError as e:
|
|
47
|
+
raise yaml.YAMLError(f"Failed to parse copier.yml: {e}") from e
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def parse_exclusion_pattern(pattern: str, component: str) -> str | None:
|
|
51
|
+
"""
|
|
52
|
+
Parse a Jinja2 exclusion pattern to extract the file path for a component.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
pattern: Jinja2 pattern like "{% if not include_scheduler %}path/to/file{% endif %}"
|
|
56
|
+
component: Component name to match (e.g., "scheduler", "worker")
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
Extracted file path, or None if pattern doesn't match the component
|
|
60
|
+
|
|
61
|
+
Examples:
|
|
62
|
+
>>> parse_exclusion_pattern(
|
|
63
|
+
... "{% if not include_scheduler %}{{ project_slug }}/app/components/scheduler{% endif %}",
|
|
64
|
+
... "scheduler"
|
|
65
|
+
... )
|
|
66
|
+
"app/components/scheduler"
|
|
67
|
+
|
|
68
|
+
>>> parse_exclusion_pattern(
|
|
69
|
+
... "{% if scheduler_backend == 'memory' -%}{{ project_slug }}/app/services/scheduler{% endif %}",
|
|
70
|
+
... "scheduler"
|
|
71
|
+
... )
|
|
72
|
+
"app/services/scheduler"
|
|
73
|
+
"""
|
|
74
|
+
# Check if pattern references this component
|
|
75
|
+
if f"include_{component}" not in pattern and component not in pattern:
|
|
76
|
+
return None
|
|
77
|
+
|
|
78
|
+
# Extract path from pattern
|
|
79
|
+
# Patterns look like: "{% if condition %}{{ project_slug }}/path/to/file{% endif %}"
|
|
80
|
+
# We want to extract: "path/to/file"
|
|
81
|
+
|
|
82
|
+
# Match: {% if ... %}...{{ project_slug }}/PATH{% endif %}
|
|
83
|
+
match = re.search(
|
|
84
|
+
r"\{%\s*if\s+.+?\s*%\}\{\{\s*project_slug\s*\}\}/(.+?)\{%\s*endif\s*%\}",
|
|
85
|
+
pattern,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
if match:
|
|
89
|
+
# Remove any trailing wildcards or special characters
|
|
90
|
+
path = match.group(1).rstrip("*")
|
|
91
|
+
return path
|
|
92
|
+
|
|
93
|
+
return None
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _expand_directories_to_files(paths: list[str]) -> list[str]:
|
|
97
|
+
"""
|
|
98
|
+
Expand directory paths to include all nested files.
|
|
99
|
+
|
|
100
|
+
For each directory path, recursively discover all files within it
|
|
101
|
+
by scanning the template directory.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
paths: List of file/directory paths (e.g., ["app/components/scheduler", "app/core/db.py"])
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
Expanded list with all nested files discovered
|
|
108
|
+
|
|
109
|
+
Example:
|
|
110
|
+
>>> _expand_directories_to_files(["app/components/scheduler"])
|
|
111
|
+
["app/components/scheduler/__init__.py", "app/components/scheduler/main.py"]
|
|
112
|
+
"""
|
|
113
|
+
template_path = get_template_path()
|
|
114
|
+
expanded_paths: list[str] = []
|
|
115
|
+
|
|
116
|
+
for path in paths:
|
|
117
|
+
# Full path in template: template/{{ project_slug }}/path
|
|
118
|
+
template_dir = template_path / PROJECT_SLUG_PLACEHOLDER / path
|
|
119
|
+
|
|
120
|
+
if template_dir.exists() and template_dir.is_dir():
|
|
121
|
+
# Recursively find all files in this directory
|
|
122
|
+
for file_path in template_dir.rglob("*"):
|
|
123
|
+
if file_path.is_file():
|
|
124
|
+
# Convert back to relative path
|
|
125
|
+
# /path/to/template/{{ project_slug }}/app/components/scheduler/main.py.jinja
|
|
126
|
+
# -> app/components/scheduler/main.py.jinja
|
|
127
|
+
relative_path = file_path.relative_to(
|
|
128
|
+
template_path / PROJECT_SLUG_PLACEHOLDER
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
# Remove .jinja extension for the final path
|
|
132
|
+
path_str = str(relative_path)
|
|
133
|
+
if path_str.endswith(JINJA_EXTENSION):
|
|
134
|
+
path_str = path_str[: -len(JINJA_EXTENSION)]
|
|
135
|
+
|
|
136
|
+
expanded_paths.append(path_str)
|
|
137
|
+
else:
|
|
138
|
+
# Not a directory or doesn't exist - keep as-is (it's a file path)
|
|
139
|
+
expanded_paths.append(path)
|
|
140
|
+
|
|
141
|
+
return expanded_paths
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def get_component_files(
|
|
145
|
+
component: str, backend_variant: str | None = None
|
|
146
|
+
) -> list[str]:
|
|
147
|
+
"""
|
|
148
|
+
Get list of file paths that belong to a component.
|
|
149
|
+
|
|
150
|
+
Uses the centralized component file mapping from post_gen_tasks.py
|
|
151
|
+
to ensure consistency between generation and updates.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
component: Component name (e.g., "scheduler", "worker", "database")
|
|
155
|
+
backend_variant: Optional backend variant (e.g., "memory", "sqlite") for scheduler
|
|
156
|
+
|
|
157
|
+
Returns:
|
|
158
|
+
List of file paths relative to project root
|
|
159
|
+
|
|
160
|
+
Examples:
|
|
161
|
+
>>> get_component_files("scheduler")
|
|
162
|
+
['app/components/scheduler', 'app/entrypoints/scheduler.py', ...]
|
|
163
|
+
|
|
164
|
+
>>> get_component_files("scheduler", "sqlite")
|
|
165
|
+
['app/services/scheduler', 'app/cli/tasks.py', ...]
|
|
166
|
+
"""
|
|
167
|
+
from .post_gen_tasks import get_component_file_mapping
|
|
168
|
+
|
|
169
|
+
mapping = get_component_file_mapping()
|
|
170
|
+
|
|
171
|
+
# Get base component files
|
|
172
|
+
component_files = mapping.get(component, []).copy()
|
|
173
|
+
|
|
174
|
+
# For scheduler, handle backend variants
|
|
175
|
+
if component == "scheduler" and backend_variant == "sqlite":
|
|
176
|
+
# Add persistence files for sqlite backend
|
|
177
|
+
persistence_files = mapping.get("scheduler_persistence", [])
|
|
178
|
+
component_files.extend(persistence_files)
|
|
179
|
+
|
|
180
|
+
# Expand directories to include all nested files
|
|
181
|
+
component_files = _expand_directories_to_files(component_files)
|
|
182
|
+
|
|
183
|
+
return sorted(set(component_files))
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def get_all_component_files() -> dict[str, list[str]]:
|
|
187
|
+
"""
|
|
188
|
+
Get file mappings for all components.
|
|
189
|
+
|
|
190
|
+
Returns:
|
|
191
|
+
Dictionary mapping component names to their file paths
|
|
192
|
+
|
|
193
|
+
Example:
|
|
194
|
+
>>> files = get_all_component_files()
|
|
195
|
+
>>> files["scheduler"]
|
|
196
|
+
['app/components/scheduler', 'app/entrypoints/scheduler.py', ...]
|
|
197
|
+
"""
|
|
198
|
+
# List of known components (from copier.yml variables)
|
|
199
|
+
components = ["scheduler", "worker", "database", "auth", "ai"]
|
|
200
|
+
|
|
201
|
+
result: dict[str, list[str]] = {}
|
|
202
|
+
|
|
203
|
+
for component in components:
|
|
204
|
+
files = get_component_files(component)
|
|
205
|
+
if files:
|
|
206
|
+
result[component] = files
|
|
207
|
+
|
|
208
|
+
# Add scheduler backend variants
|
|
209
|
+
result["scheduler_memory"] = get_component_files("scheduler", "memory")
|
|
210
|
+
result["scheduler_sqlite"] = get_component_files("scheduler", "sqlite")
|
|
211
|
+
|
|
212
|
+
return result
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def get_service_files(service: str) -> list[str]:
|
|
216
|
+
"""
|
|
217
|
+
Get list of file paths that belong to a service.
|
|
218
|
+
|
|
219
|
+
Services are components that provide business logic (auth, ai).
|
|
220
|
+
This is an alias for get_component_files for clarity.
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
service: Service name (e.g., "auth", "ai")
|
|
224
|
+
|
|
225
|
+
Returns:
|
|
226
|
+
List of file paths relative to project root
|
|
227
|
+
"""
|
|
228
|
+
return get_component_files(service)
|