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.
Files changed (392) hide show
  1. aegis/__init__.py +5 -0
  2. aegis/__main__.py +51 -0
  3. aegis/cli/__init__.py +6 -0
  4. aegis/cli/callbacks.py +114 -0
  5. aegis/cli/interactive.py +611 -0
  6. aegis/cli/utils.py +70 -0
  7. aegis/cli/validators.py +34 -0
  8. aegis/commands/__init__.py +6 -0
  9. aegis/commands/add.py +353 -0
  10. aegis/commands/add_service.py +332 -0
  11. aegis/commands/components.py +35 -0
  12. aegis/commands/init.py +370 -0
  13. aegis/commands/remove.py +227 -0
  14. aegis/commands/services.py +52 -0
  15. aegis/commands/update.py +252 -0
  16. aegis/commands/version.py +12 -0
  17. aegis/config/__init__.py +1 -0
  18. aegis/config/shared_files.py +136 -0
  19. aegis/core/CLAUDE.md +377 -0
  20. aegis/core/__init__.py +6 -0
  21. aegis/core/component_files.py +228 -0
  22. aegis/core/component_utils.py +220 -0
  23. aegis/core/components.py +127 -0
  24. aegis/core/copier_manager.py +315 -0
  25. aegis/core/copier_updater.py +475 -0
  26. aegis/core/dependency_resolver.py +119 -0
  27. aegis/core/manual_updater.py +554 -0
  28. aegis/core/post_gen_tasks.py +547 -0
  29. aegis/core/service_resolver.py +261 -0
  30. aegis/core/services.py +157 -0
  31. aegis/core/template_generator.py +266 -0
  32. aegis/core/version_compatibility.py +259 -0
  33. aegis/templates/CLAUDE.md +591 -0
  34. aegis/templates/cookiecutter-aegis-project/cookiecutter.json +39 -0
  35. aegis/templates/cookiecutter-aegis-project/hooks/post_gen_project.py +214 -0
  36. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/.dockerignore +71 -0
  37. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/.env.example.j2 +130 -0
  38. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/.gitignore +131 -0
  39. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/Dockerfile +53 -0
  40. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/Makefile +236 -0
  41. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/README.md.j2 +196 -0
  42. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/alembic/alembic.ini.j2 +111 -0
  43. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/alembic/env.py.j2 +91 -0
  44. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/alembic/script.py.mako +25 -0
  45. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/alembic/versions/001_initial_auth.py.j2 +51 -0
  46. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/__init__.py +5 -0
  47. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/__init__.py +6 -0
  48. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/ai.py.j2 +700 -0
  49. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/ai_rendering.py +361 -0
  50. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/auth.py.j2 +253 -0
  51. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/health.py.j2 +419 -0
  52. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/load_test.py.j2 +656 -0
  53. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/main.py.j2 +65 -0
  54. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/marko_terminal_renderer.py +489 -0
  55. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/tasks.py.j2 +328 -0
  56. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/{% if cookiecutter.include_scheduler == /"yes/" %}tasks.py{% endif %}" +340 -0
  57. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/__init__.py +0 -0
  58. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/__init__.py +0 -0
  59. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/ai/__init__.py +8 -0
  60. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/ai/router.py +329 -0
  61. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/auth/__init__.py +1 -0
  62. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/auth/router.py +64 -0
  63. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/deps.py +58 -0
  64. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/health.py +163 -0
  65. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/models.py.j2 +280 -0
  66. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/routing.py.j2 +32 -0
  67. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/scheduler.py.j2 +121 -0
  68. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/worker.py.j2 +478 -0
  69. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/hooks.py +144 -0
  70. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/main.py +31 -0
  71. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/middleware/__init__.py +1 -0
  72. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/middleware/cors.py +20 -0
  73. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/shutdown/__init__.py +1 -0
  74. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/shutdown/cleanup.py +14 -0
  75. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/startup/__init__.py +1 -0
  76. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/startup/component_health.py.j2 +418 -0
  77. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/startup/database_init.py.j2 +83 -0
  78. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/__init__.py +5 -0
  79. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/controls/__init__.py +27 -0
  80. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/controls/table.py +78 -0
  81. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/controls/text.py +142 -0
  82. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/__init__.py.j2 +47 -0
  83. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/ai_card.py +287 -0
  84. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/auth_card.py +198 -0
  85. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/base_card.py +256 -0
  86. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/card_factory.py +227 -0
  87. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/card_utils.py +333 -0
  88. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/database_card.py +420 -0
  89. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/fastapi_card.py +328 -0
  90. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/flet_card.py +267 -0
  91. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/redis_card.py +322 -0
  92. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/scheduler_card.py +352 -0
  93. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/services_card.py +233 -0
  94. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/worker_card.py +684 -0
  95. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/main.py.j2 +653 -0
  96. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/theme.py +48 -0
  97. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/scheduler/__init__.py +1 -0
  98. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/scheduler/main.py.j2 +156 -0
  99. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/CLAUDE.md.j2 +213 -0
  100. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/__init__.py +6 -0
  101. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/constants.py.j2 +30 -0
  102. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/pools.py +97 -0
  103. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/queues/__init__.py +1 -0
  104. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/queues/load_test.py +55 -0
  105. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/queues/media.py +49 -0
  106. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/queues/system.py +44 -0
  107. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/registry.py +139 -0
  108. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/tasks/__init__.py +120 -0
  109. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/tasks/load_tasks.py +507 -0
  110. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/tasks/simple_system_tasks.py +33 -0
  111. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/tasks/system_tasks.py +281 -0
  112. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/core/config.py.j2 +178 -0
  113. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/core/constants.py +58 -0
  114. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/core/db.py.j2 +176 -0
  115. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/core/log.py +92 -0
  116. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/core/security.py +62 -0
  117. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/entrypoints/__init__.py +1 -0
  118. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/entrypoints/webserver.py +40 -0
  119. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/entrypoints/{% if cookiecutter.include_scheduler == /"yes/" %}scheduler.py{% endif %}" +21 -0
  120. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/integrations/__init__.py +0 -0
  121. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/integrations/main.py +62 -0
  122. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/models/__init__.py +1 -0
  123. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/models/user.py +44 -0
  124. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/py.typed +0 -0
  125. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/__init__.py +1 -0
  126. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/__init__.py +8 -0
  127. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/config.py +130 -0
  128. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/conversation.py +213 -0
  129. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/health.py +96 -0
  130. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/models.py +229 -0
  131. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/providers.py +370 -0
  132. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/service.py +388 -0
  133. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/auth/__init__.py +1 -0
  134. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/auth/auth_service.py +41 -0
  135. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/auth/health.py +164 -0
  136. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/auth/user_service.py +83 -0
  137. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/backend/middleware_inspector.py.j2 +223 -0
  138. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/backend/models.py.j2 +70 -0
  139. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/backend/route_inspector.py.j2 +155 -0
  140. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/load_test.py +679 -0
  141. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/load_test_models.py +266 -0
  142. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/scheduler/__init__.py.j2 +21 -0
  143. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/scheduler/models.py.j2 +119 -0
  144. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/scheduler/scheduled_task_manager.py.j2 +273 -0
  145. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/scheduler/task_monitor.py.j2 +189 -0
  146. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/shared/__init__.py +15 -0
  147. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/shared/models.py +26 -0
  148. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/__init__.py +52 -0
  149. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/alerts.py +94 -0
  150. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/backup.py.j2 +119 -0
  151. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/health.py.j2 +1333 -0
  152. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/models.py +243 -0
  153. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/ui.py +52 -0
  154. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/assets/aegis-manifesto-dark.png +0 -0
  155. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/assets/aegis-manifesto-square-backup.png +0 -0
  156. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/assets/aegis-manifesto.png +0 -0
  157. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/.dockerignore +71 -0
  158. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/.env.example.j2 +64 -0
  159. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/.gitignore +131 -0
  160. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/Dockerfile +53 -0
  161. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/Makefile +211 -0
  162. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/README.md.j2 +172 -0
  163. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/docker-compose.yml.j2 +78 -0
  164. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/mkdocs.yml.j2 +62 -0
  165. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/pyproject.toml.j2 +120 -0
  166. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/uv.lock +1673 -0
  167. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docker-compose.yml.j2 +200 -0
  168. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/api.md +191 -0
  169. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/components/scheduler.md +0 -0
  170. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/components/scheduler.md.j2 +621 -0
  171. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/development.md +215 -0
  172. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/health.md +240 -0
  173. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/javascripts/mermaid-config.js +62 -0
  174. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/stylesheets/mermaid.css +95 -0
  175. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/mkdocs.yml.j2 +62 -0
  176. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/pyproject.toml.j2 +131 -0
  177. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/scripts/entrypoint.sh +87 -0
  178. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/scripts/entrypoint.sh.j2 +93 -0
  179. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/scripts/gen_docs.py +16 -0
  180. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/api/__init__.py +1 -0
  181. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/api/test_auth_endpoints.py.j2 +307 -0
  182. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/api/test_health_endpoints.py.j2 +262 -0
  183. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/api/test_scheduler_endpoints.py.j2 +214 -0
  184. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/api/test_worker_endpoints.py.j2 +165 -0
  185. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/cli/test_ai_rendering.py +427 -0
  186. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/cli/test_conversation_memory.py +465 -0
  187. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/components/test_scheduler.py +43 -0
  188. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/conftest.py.j2 +195 -0
  189. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/__init__.py +1 -0
  190. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/ai/__init__.py +1 -0
  191. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/ai/conftest.py +78 -0
  192. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/ai/test_health.py +157 -0
  193. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/ai/test_models.py +164 -0
  194. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/ai/test_service.py +198 -0
  195. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_auth_integration.py.j2 +528 -0
  196. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_component_integration.py.j2 +387 -0
  197. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_conversation_persistence.py +342 -0
  198. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_health_logic.py.j2 +663 -0
  199. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_load_test_models.py +619 -0
  200. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_load_test_service.py +603 -0
  201. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_middleware_inspector.py.j2 +248 -0
  202. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_scheduled_task_manager.py.j2 +292 -0
  203. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_system_service.py +98 -0
  204. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_worker_health_registration.py.j2 +257 -0
  205. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/test_core.py +49 -0
  206. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/uv.lock +1673 -0
  207. aegis/templates/copier-aegis-project/{{ project_slug }}/.copier-answers.yml.jinja +21 -0
  208. aegis/templates/copier-aegis-project/{{ project_slug }}/.dockerignore +71 -0
  209. aegis/templates/copier-aegis-project/{{ project_slug }}/.env.example.jinja +130 -0
  210. aegis/templates/copier-aegis-project/{{ project_slug }}/.gitignore +131 -0
  211. aegis/templates/copier-aegis-project/{{ project_slug }}/Dockerfile +53 -0
  212. aegis/templates/copier-aegis-project/{{ project_slug }}/Makefile.jinja +236 -0
  213. aegis/templates/copier-aegis-project/{{ project_slug }}/README.md.jinja +196 -0
  214. aegis/templates/copier-aegis-project/{{ project_slug }}/alembic/alembic.ini.jinja +111 -0
  215. aegis/templates/copier-aegis-project/{{ project_slug }}/alembic/env.py.jinja +91 -0
  216. aegis/templates/copier-aegis-project/{{ project_slug }}/alembic/script.py.mako +25 -0
  217. aegis/templates/copier-aegis-project/{{ project_slug }}/alembic/versions/001_initial_auth.py.jinja +51 -0
  218. aegis/templates/copier-aegis-project/{{ project_slug }}/app/__init__.py.jinja +5 -0
  219. aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/__init__.py.jinja +6 -0
  220. aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/ai.py.jinja +700 -0
  221. aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/ai_rendering.py +360 -0
  222. aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/auth.py.jinja +253 -0
  223. aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/health.py.jinja +419 -0
  224. aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/load_test.py.jinja +656 -0
  225. aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/main.py.jinja +65 -0
  226. aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/marko_terminal_renderer.py +489 -0
  227. aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/tasks.py.jinja +328 -0
  228. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/__init__.py +0 -0
  229. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/__init__.py +0 -0
  230. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/ai/__init__.py +8 -0
  231. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/ai/router.py +329 -0
  232. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/auth/__init__.py +1 -0
  233. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/auth/router.py +64 -0
  234. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/deps.py +58 -0
  235. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/health.py.jinja +163 -0
  236. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/models.py.jinja +280 -0
  237. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/routing.py.jinja +32 -0
  238. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/scheduler.py.jinja +121 -0
  239. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/worker.py.jinja +478 -0
  240. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/hooks.py +144 -0
  241. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/main.py +31 -0
  242. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/middleware/__init__.py +1 -0
  243. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/middleware/cors.py +20 -0
  244. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/shutdown/__init__.py +1 -0
  245. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/shutdown/cleanup.py +14 -0
  246. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/startup/__init__.py +1 -0
  247. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/startup/component_health.py.jinja +418 -0
  248. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/startup/database_init.py.jinja +83 -0
  249. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/__init__.py +5 -0
  250. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/controls/__init__.py +27 -0
  251. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/controls/table.py +78 -0
  252. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/controls/text.py +142 -0
  253. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/__init__.py.jinja +47 -0
  254. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/ai_card.py +287 -0
  255. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/auth_card.py +198 -0
  256. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/base_card.py +256 -0
  257. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/card_factory.py +227 -0
  258. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/card_utils.py +333 -0
  259. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/database_card.py +420 -0
  260. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/fastapi_card.py +328 -0
  261. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/flet_card.py +267 -0
  262. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/redis_card.py +322 -0
  263. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/scheduler_card.py +352 -0
  264. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/services_card.py +233 -0
  265. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/worker_card.py +684 -0
  266. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/main.py.jinja +653 -0
  267. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/theme.py +48 -0
  268. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/scheduler/__init__.py +1 -0
  269. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/scheduler/main.py.jinja +156 -0
  270. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/CLAUDE.md.jinja +213 -0
  271. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/__init__.py +6 -0
  272. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/constants.py.jinja +30 -0
  273. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/pools.py +97 -0
  274. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/queues/__init__.py +1 -0
  275. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/queues/load_test.py +55 -0
  276. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/queues/media.py +49 -0
  277. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/queues/system.py +44 -0
  278. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/registry.py +139 -0
  279. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/tasks/__init__.py +120 -0
  280. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/tasks/load_tasks.py +507 -0
  281. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/tasks/simple_system_tasks.py +33 -0
  282. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/tasks/system_tasks.py +281 -0
  283. aegis/templates/copier-aegis-project/{{ project_slug }}/app/core/config.py.jinja +178 -0
  284. aegis/templates/copier-aegis-project/{{ project_slug }}/app/core/constants.py +58 -0
  285. aegis/templates/copier-aegis-project/{{ project_slug }}/app/core/db.py.jinja +176 -0
  286. aegis/templates/copier-aegis-project/{{ project_slug }}/app/core/log.py +92 -0
  287. aegis/templates/copier-aegis-project/{{ project_slug }}/app/core/security.py +62 -0
  288. aegis/templates/copier-aegis-project/{{ project_slug }}/app/entrypoints/__init__.py +1 -0
  289. aegis/templates/copier-aegis-project/{{ project_slug }}/app/entrypoints/scheduler.py.jinja +21 -0
  290. aegis/templates/copier-aegis-project/{{ project_slug }}/app/entrypoints/webserver.py +39 -0
  291. aegis/templates/copier-aegis-project/{{ project_slug }}/app/integrations/__init__.py +0 -0
  292. aegis/templates/copier-aegis-project/{{ project_slug }}/app/integrations/main.py +61 -0
  293. aegis/templates/copier-aegis-project/{{ project_slug }}/app/models/__init__.py +1 -0
  294. aegis/templates/copier-aegis-project/{{ project_slug }}/app/models/user.py +44 -0
  295. aegis/templates/copier-aegis-project/{{ project_slug }}/app/py.typed +0 -0
  296. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/__init__.py +1 -0
  297. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/__init__.py +8 -0
  298. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/config.py +130 -0
  299. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/conversation.py +213 -0
  300. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/health.py +96 -0
  301. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/models.py +229 -0
  302. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/providers.py.jinja +370 -0
  303. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/service.py +387 -0
  304. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/auth/__init__.py +1 -0
  305. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/auth/auth_service.py +40 -0
  306. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/auth/health.py +162 -0
  307. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/auth/user_service.py +82 -0
  308. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/backend/middleware_inspector.py.jinja +223 -0
  309. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/backend/models.py.jinja +70 -0
  310. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/backend/route_inspector.py.jinja +155 -0
  311. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/load_test.py +678 -0
  312. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/load_test_models.py +265 -0
  313. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/scheduler/__init__.py.jinja +21 -0
  314. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/scheduler/models.py.jinja +119 -0
  315. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/scheduler/scheduled_task_manager.py.jinja +273 -0
  316. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/scheduler/task_monitor.py.jinja +189 -0
  317. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/shared/__init__.py +15 -0
  318. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/shared/models.py +26 -0
  319. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/system/__init__.py +52 -0
  320. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/system/alerts.py +94 -0
  321. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/system/backup.py.jinja +119 -0
  322. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/system/health.py.jinja +1333 -0
  323. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/system/models.py +243 -0
  324. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/system/ui.py +52 -0
  325. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57223!aegis-manifesto.png +0 -0
  326. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57224!aegis-manifesto-dark.png +0 -0
  327. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57225!aegis-manifesto-square-backup.png +0 -0
  328. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57533!aegis-manifesto.png +0 -0
  329. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57534!aegis-manifesto-dark.png +0 -0
  330. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57538!aegis-manifesto-square-backup.png +0 -0
  331. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57897!aegis-manifesto.png +0 -0
  332. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57898!aegis-manifesto-dark.png +0 -0
  333. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57904!aegis-manifesto-square-backup.png +0 -0
  334. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!58315!aegis-manifesto.png +0 -0
  335. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!58316!aegis-manifesto-dark.png +0 -0
  336. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!58324!aegis-manifesto-square-backup.png +0 -0
  337. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!58837!aegis-manifesto.png +0 -0
  338. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!58838!aegis-manifesto-dark.png +0 -0
  339. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!58849!aegis-manifesto-square-backup.png +0 -0
  340. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/aegis-manifesto-dark.png +0 -0
  341. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/aegis-manifesto-square-backup.png +0 -0
  342. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/aegis-manifesto.png +0 -0
  343. aegis/templates/copier-aegis-project/{{ project_slug }}/clean-validation/.env.example.jinja +64 -0
  344. aegis/templates/copier-aegis-project/{{ project_slug }}/clean-validation/README.md.jinja +172 -0
  345. aegis/templates/copier-aegis-project/{{ project_slug }}/clean-validation/docker-compose.yml.jinja +78 -0
  346. aegis/templates/copier-aegis-project/{{ project_slug }}/clean-validation/mkdocs.yml.jinja +62 -0
  347. aegis/templates/copier-aegis-project/{{ project_slug }}/clean-validation/pyproject.toml.jinja +120 -0
  348. aegis/templates/copier-aegis-project/{{ project_slug }}/docker-compose.yml.jinja +200 -0
  349. aegis/templates/copier-aegis-project/{{ project_slug }}/docs/api.md.jinja +191 -0
  350. aegis/templates/copier-aegis-project/{{ project_slug }}/docs/components/scheduler.md +0 -0
  351. aegis/templates/copier-aegis-project/{{ project_slug }}/docs/components/scheduler.md.jinja +621 -0
  352. aegis/templates/copier-aegis-project/{{ project_slug }}/docs/development.md.jinja +215 -0
  353. aegis/templates/copier-aegis-project/{{ project_slug }}/docs/health.md.jinja +240 -0
  354. aegis/templates/copier-aegis-project/{{ project_slug }}/docs/javascripts/mermaid-config.js +62 -0
  355. aegis/templates/copier-aegis-project/{{ project_slug }}/docs/stylesheets/mermaid.css +95 -0
  356. aegis/templates/copier-aegis-project/{{ project_slug }}/mkdocs.yml.jinja +62 -0
  357. aegis/templates/copier-aegis-project/{{ project_slug }}/pyproject.toml.jinja +131 -0
  358. aegis/templates/copier-aegis-project/{{ project_slug }}/scripts/entrypoint.sh +87 -0
  359. aegis/templates/copier-aegis-project/{{ project_slug }}/scripts/entrypoint.sh.jinja +93 -0
  360. aegis/templates/copier-aegis-project/{{ project_slug }}/scripts/gen_docs.py +16 -0
  361. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/api/__init__.py +1 -0
  362. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/api/test_auth_endpoints.py.jinja +307 -0
  363. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/api/test_health_endpoints.py.jinja +262 -0
  364. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/api/test_scheduler_endpoints.py.jinja +214 -0
  365. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/api/test_worker_endpoints.py.jinja +165 -0
  366. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/cli/test_ai_rendering.py +427 -0
  367. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/cli/test_conversation_memory.py +465 -0
  368. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/components/test_scheduler.py +43 -0
  369. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/conftest.py.jinja +195 -0
  370. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/__init__.py +1 -0
  371. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/ai/__init__.py +1 -0
  372. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/ai/conftest.py +78 -0
  373. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/ai/test_health.py +157 -0
  374. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/ai/test_models.py +164 -0
  375. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/ai/test_service.py +198 -0
  376. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_auth_integration.py.jinja +528 -0
  377. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_component_integration.py.jinja +387 -0
  378. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_conversation_persistence.py +342 -0
  379. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_health_logic.py.jinja +663 -0
  380. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_load_test_models.py +619 -0
  381. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_load_test_service.py +603 -0
  382. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_middleware_inspector.py.jinja +248 -0
  383. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_scheduled_task_manager.py.jinja +292 -0
  384. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_system_service.py +98 -0
  385. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_worker_health_registration.py.jinja +257 -0
  386. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/test_core.py +49 -0
  387. aegis/templates/copier-aegis-project/{{ project_slug }}/uv.lock +1673 -0
  388. aegis_stack-0.2.0rc2.dist-info/METADATA +165 -0
  389. aegis_stack-0.2.0rc2.dist-info/RECORD +392 -0
  390. aegis_stack-0.2.0rc2.dist-info/WHEEL +4 -0
  391. aegis_stack-0.2.0rc2.dist-info/entry_points.txt +3 -0
  392. aegis_stack-0.2.0rc2.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,611 @@
1
+ """
2
+ Interactive CLI components.
3
+
4
+ This module contains interactive selection and prompting functions
5
+ used by CLI commands.
6
+ """
7
+
8
+ from pathlib import Path
9
+
10
+ import typer
11
+
12
+ from ..core.components import COMPONENTS, CORE_COMPONENTS, ComponentSpec, ComponentType
13
+ from ..core.services import SERVICES, ServiceType, get_services_by_type
14
+
15
+ # Global variable to store AI provider selections for template generation
16
+ _ai_provider_selection: dict[str, list[str]] = {}
17
+
18
+
19
+ def get_interactive_infrastructure_components() -> list[ComponentSpec]:
20
+ """Get infrastructure components available for interactive selection."""
21
+ # Get all infrastructure components
22
+ infra_components = []
23
+ for component_spec in COMPONENTS.values():
24
+ if component_spec.type == ComponentType.INFRASTRUCTURE:
25
+ infra_components.append(component_spec)
26
+
27
+ # Sort by name for consistent ordering
28
+ return sorted(infra_components, key=lambda x: x.name)
29
+
30
+
31
+ def interactive_project_selection() -> tuple[list[str], str, list[str]]:
32
+ """
33
+ Interactive project selection with component and service options.
34
+
35
+ Returns:
36
+ Tuple of (selected_components, scheduler_backend, selected_services)
37
+ """
38
+
39
+ typer.echo("šŸŽÆ Component Selection")
40
+ typer.echo("=" * 40)
41
+ typer.echo(
42
+ f"āœ… Core components ({' + '.join(CORE_COMPONENTS)}) included automatically\n"
43
+ )
44
+
45
+ selected = []
46
+ database_engine = None # Track database engine selection
47
+ database_added_by_scheduler = False # Track if database was added by scheduler
48
+ scheduler_backend = "memory" # Track scheduler backend: memory, sqlite, postgres
49
+
50
+ # Get all infrastructure components from registry
51
+ infra_components = get_interactive_infrastructure_components()
52
+
53
+ typer.echo("šŸ—ļø Infrastructure Components:")
54
+
55
+ # Process components in a specific order to handle dependencies
56
+ component_order = ["redis", "worker", "scheduler", "database"]
57
+
58
+ for component_name in component_order:
59
+ # Find the component spec
60
+ component_spec = next(
61
+ (c for c in infra_components if c.name == component_name), None
62
+ )
63
+ if not component_spec:
64
+ continue # Skip if component doesn't exist in registry
65
+
66
+ # Handle special worker dependency logic
67
+ if component_name == "worker":
68
+ if "redis" in selected:
69
+ # Redis already selected, simple worker prompt
70
+ prompt = f" Add {component_spec.description.lower()}?"
71
+ if typer.confirm(prompt):
72
+ selected.append("worker")
73
+ else:
74
+ # Redis not selected, offer to add both
75
+ prompt = (
76
+ f" Add {component_spec.description.lower()}? (will auto-add Redis)"
77
+ )
78
+ if typer.confirm(prompt):
79
+ selected.extend(["redis", "worker"])
80
+ elif component_name == "scheduler":
81
+ # Enhanced scheduler selection with persistence and database options
82
+ prompt = f" Add {component_spec.description}?"
83
+ if typer.confirm(prompt):
84
+ selected.append("scheduler")
85
+
86
+ # Follow-up: persistence question
87
+ typer.echo("\nšŸ’¾ Scheduler Persistence:")
88
+ persistence_prompt = (
89
+ " Do you want to persist scheduled jobs? "
90
+ "(Enables job history, recovery after restarts)"
91
+ )
92
+ if typer.confirm(persistence_prompt):
93
+ # Database engine selection (SQLite only for now)
94
+ typer.echo("\nšŸ—ƒļø Database Engine:")
95
+ typer.echo(" SQLite will be configured for job persistence")
96
+ typer.echo(" (PostgreSQL support coming in future releases)")
97
+
98
+ # Show SQLite limitations
99
+ typer.echo("\nāš ļø SQLite Limitations:")
100
+ typer.echo(
101
+ " • Multi-container API access works in development only "
102
+ "(shared volumes)"
103
+ )
104
+ typer.echo(" • Production deployment will be single-container")
105
+ typer.echo(
106
+ " • Use PostgreSQL for full production multi-container support"
107
+ )
108
+
109
+ if typer.confirm(" Continue with SQLite?", default=True):
110
+ database_engine = "sqlite"
111
+ selected.append("database")
112
+ database_added_by_scheduler = True
113
+ # Mark scheduler backend as sqlite
114
+ scheduler_backend = "sqlite"
115
+ typer.echo("āœ… Scheduler + SQLite database configured")
116
+
117
+ # Show bonus backup job message only when database is added
118
+ typer.echo("\nšŸŽÆ Bonus: Adding database backup job")
119
+ typer.echo(
120
+ "āœ… Scheduled daily database backup job included "
121
+ "(runs at 2 AM)"
122
+ )
123
+ else:
124
+ typer.echo("ā¹ļø Scheduler persistence cancelled")
125
+ # Don't add database if user declines SQLite
126
+
127
+ typer.echo() # Extra spacing after scheduler section
128
+ elif component_name == "database":
129
+ # Skip generic database prompt if already added by scheduler
130
+ if database_added_by_scheduler:
131
+ continue
132
+
133
+ # Standard database prompt (when not added by scheduler)
134
+ prompt = f" Add {component_spec.description}?"
135
+ if typer.confirm(prompt):
136
+ selected.append("database")
137
+
138
+ # Show bonus backup job message when database added with scheduler
139
+ if "scheduler" in selected:
140
+ typer.echo("\nšŸŽÆ Bonus: Adding database backup job")
141
+ typer.echo(
142
+ "āœ… Scheduled daily database backup job included (runs at 2 AM)"
143
+ )
144
+ else:
145
+ # Standard prompt for other components
146
+ prompt = f" Add {component_spec.description}?"
147
+ if typer.confirm(prompt):
148
+ selected.append(component_name)
149
+
150
+ # Update selected list with engine info for display
151
+ if "database" in selected and database_engine:
152
+ # Replace "database" with formatted version for display
153
+ db_index = selected.index("database")
154
+ selected[db_index] = f"database[{database_engine}]"
155
+
156
+ # Update scheduler with backend info if not memory
157
+ if "scheduler" in selected and scheduler_backend != "memory":
158
+ scheduler_index = selected.index("scheduler")
159
+ selected[scheduler_index] = f"scheduler[{scheduler_backend}]"
160
+
161
+ # Service selection
162
+ selected_services = []
163
+
164
+ if SERVICES: # Only show services if any are available
165
+ typer.echo("\nšŸ”§ Service Selection")
166
+ typer.echo("=" * 40)
167
+ typer.echo(
168
+ "Services provide business logic functionality for your application.\n"
169
+ )
170
+
171
+ # Group services by type for better organization
172
+ auth_services = get_services_by_type(ServiceType.AUTH)
173
+
174
+ if auth_services:
175
+ typer.echo("šŸ” Authentication Services:")
176
+ for service_name, service_spec in auth_services.items():
177
+ prompt = f" Add {service_spec.description.lower()}?"
178
+ if typer.confirm(prompt):
179
+ # Auth service requires database - provide explicit confirmation
180
+ typer.echo("\nšŸ—ƒļø Database Required:")
181
+ typer.echo(" Authentication requires a database for user storage")
182
+ typer.echo(" (user accounts, sessions, JWT tokens)")
183
+
184
+ # Check if database is already selected
185
+ database_already_selected = any(
186
+ "database" in comp for comp in selected
187
+ )
188
+
189
+ if database_already_selected:
190
+ typer.echo("āœ… Database component already selected")
191
+ selected_services.append(service_name)
192
+ else:
193
+ auth_confirm_prompt = " Continue and add database component?"
194
+ if typer.confirm(auth_confirm_prompt, default=True):
195
+ selected_services.append(service_name)
196
+ # Note: Database will be auto-added by service resolution in init.py
197
+ typer.echo("āœ… Authentication + Database configured")
198
+ else:
199
+ typer.echo("ā¹ļø Authentication service cancelled")
200
+
201
+ # AI & Machine Learning Services
202
+ ai_services = get_services_by_type(ServiceType.AI)
203
+
204
+ if ai_services:
205
+ typer.echo("\nšŸ¤– AI & Machine Learning Services:")
206
+ for service_name, service_spec in ai_services.items():
207
+ prompt = f" Add {service_spec.description.lower()}?"
208
+ if typer.confirm(prompt):
209
+ # AI service requires backend (always available) - no dependency issues
210
+ typer.echo("\nšŸ¤– AI Provider Selection:")
211
+ typer.echo(
212
+ " Choose AI providers to include (multiple selection supported)"
213
+ )
214
+ typer.echo(" šŸ“‹ Provider Options:")
215
+
216
+ # Provider selection with recommendations
217
+ providers = []
218
+ provider_info = [
219
+ ("openai", "OpenAI", "GPT models", "šŸ’° Paid", False),
220
+ ("anthropic", "Anthropic", "Claude models", "šŸ’° Paid", False),
221
+ ("google", "Google", "Gemini models", "šŸ†“ Free tier", True),
222
+ ("groq", "Groq", "Fast inference", "šŸ†“ Free tier", True),
223
+ ("mistral", "Mistral", "Open models", "šŸ’° Mostly paid", False),
224
+ (
225
+ "cohere",
226
+ "Cohere",
227
+ "Enterprise focus",
228
+ "šŸ’° Limited free",
229
+ False,
230
+ ),
231
+ ]
232
+
233
+ # Ask about each provider
234
+ for (
235
+ provider_id,
236
+ name,
237
+ description,
238
+ pricing,
239
+ recommended,
240
+ ) in provider_info:
241
+ recommend_text = " (Recommended)" if recommended else ""
242
+ if typer.confirm(
243
+ f" ☐ {name} - {description} ({pricing}){recommend_text}?",
244
+ default=recommended,
245
+ ):
246
+ providers.append(provider_id)
247
+
248
+ # Handle no providers selected
249
+ if not providers:
250
+ typer.echo(
251
+ " āš ļø No providers selected, adding recommended defaults..."
252
+ )
253
+ providers = ["groq", "google"] # Safe defaults with free tiers
254
+
255
+ # Show selected providers
256
+ typer.echo(f"\n āœ… Selected providers: {', '.join(providers)}")
257
+ typer.echo(" šŸ“¦ Dependencies will be optimized for your selection")
258
+
259
+ # Store provider selection in global context for template generation
260
+ _ai_provider_selection[service_name] = providers
261
+ selected_services.append(service_name)
262
+ typer.echo("āœ… AI service configured")
263
+
264
+ # Future service types can be added here as they become available
265
+ # payment_services = get_services_by_type(ServiceType.PAYMENT)
266
+
267
+ return selected, scheduler_backend, selected_services
268
+
269
+
270
+ def get_ai_provider_selection(service_name: str = "ai") -> list[str]:
271
+ """
272
+ Get AI provider selection from interactive session.
273
+
274
+ Args:
275
+ service_name: Name of the AI service (defaults to "ai")
276
+
277
+ Returns:
278
+ List of selected provider names, or default providers if none selected
279
+ """
280
+ return _ai_provider_selection.get(service_name, ["openai"])
281
+
282
+
283
+ def clear_ai_provider_selection() -> None:
284
+ """Clear stored AI provider selection (useful for testing)."""
285
+ global _ai_provider_selection
286
+ _ai_provider_selection.clear()
287
+
288
+
289
+ def interactive_component_add_selection(project_path: Path) -> tuple[list[str], str]:
290
+ """
291
+ Interactive component selection for adding to existing project.
292
+
293
+ Shows currently enabled components (grayed out) and available
294
+ components to add (selectable). Handles dependency resolution.
295
+
296
+ Args:
297
+ project_path: Path to the existing project
298
+
299
+ Returns:
300
+ Tuple of (selected_components, scheduler_backend)
301
+ """
302
+ from ..core.copier_manager import load_copier_answers
303
+
304
+ # Load current project state
305
+ try:
306
+ current_answers = load_copier_answers(project_path)
307
+ except Exception as e:
308
+ typer.echo(f"āŒ Failed to load project configuration: {e}", err=True)
309
+ raise typer.Exit(1)
310
+
311
+ typer.echo("\nšŸŽÆ Component Selection")
312
+ typer.echo("=" * 40)
313
+
314
+ # Show currently enabled components
315
+ enabled_components = []
316
+ for component in ["redis", "worker", "scheduler", "database"]:
317
+ if current_answers.get(f"include_{component}"):
318
+ enabled_components.append(component)
319
+
320
+ if enabled_components:
321
+ typer.echo(f"āœ… Currently enabled: {', '.join(enabled_components)}")
322
+ else:
323
+ typer.echo("āœ… Currently enabled: backend, frontend (core only)")
324
+
325
+ typer.echo("\nšŸ—ļø Available Components:\n")
326
+
327
+ selected = []
328
+ scheduler_backend = "memory"
329
+
330
+ # Get all infrastructure components in order
331
+ component_order = ["redis", "worker", "scheduler", "database"]
332
+
333
+ for component_name in component_order:
334
+ # Skip if already enabled
335
+ if component_name in enabled_components:
336
+ typer.echo(f" āœ… {component_name} - Already enabled")
337
+ continue
338
+
339
+ # Skip if already selected in this session (e.g., database auto-added by scheduler)
340
+ if component_name in selected:
341
+ continue
342
+
343
+ # Find the component spec
344
+ component_spec = COMPONENTS.get(component_name)
345
+ if not component_spec:
346
+ continue
347
+
348
+ # Handle special logic for each component
349
+ if component_name == "worker":
350
+ if "redis" in enabled_components or "redis" in selected:
351
+ # Redis already available
352
+ prompt = f" Add {component_spec.description.lower()}?"
353
+ if typer.confirm(prompt):
354
+ selected.append("worker")
355
+ else:
356
+ # Need to add redis too
357
+ prompt = (
358
+ f" Add {component_spec.description.lower()}? (will auto-add Redis)"
359
+ )
360
+ if typer.confirm(prompt):
361
+ selected.extend(["redis", "worker"])
362
+
363
+ elif component_name == "scheduler":
364
+ prompt = f" Add {component_spec.description}?"
365
+ if typer.confirm(prompt):
366
+ selected.append("scheduler")
367
+
368
+ # Check if database is available or will be added
369
+ database_available = (
370
+ "database" in enabled_components or "database" in selected
371
+ )
372
+
373
+ if database_available:
374
+ # Database already available - offer persistence
375
+ typer.echo("\nšŸ’¾ Scheduler Persistence:")
376
+ if typer.confirm(" Enable job persistence with SQLite?"):
377
+ scheduler_backend = "sqlite"
378
+ typer.echo(" āœ… Scheduler will use SQLite for job persistence")
379
+ else:
380
+ typer.echo(
381
+ " ā„¹ļø Scheduler will use memory backend (no persistence)"
382
+ )
383
+ else:
384
+ # Ask if they plan to add database
385
+ typer.echo("\nšŸ’¾ Scheduler Persistence:")
386
+ typer.echo(" Job persistence requires SQLite database component")
387
+ if typer.confirm(" Add database component for job persistence?"):
388
+ selected.append("database")
389
+ scheduler_backend = "sqlite"
390
+ typer.echo(
391
+ " āœ… Database will be added - scheduler will use SQLite"
392
+ )
393
+ else:
394
+ typer.echo(
395
+ " ā„¹ļø Scheduler will use memory backend (no persistence)"
396
+ )
397
+
398
+ elif component_name == "redis":
399
+ # Only offer if not already added by worker
400
+ if "redis" not in selected:
401
+ prompt = f" Add {component_spec.description}?"
402
+ if typer.confirm(prompt):
403
+ selected.append("redis")
404
+
405
+ else:
406
+ # Standard prompt for other components
407
+ prompt = f" Add {component_spec.description}?"
408
+ if typer.confirm(prompt):
409
+ selected.append(component_name)
410
+
411
+ return selected, scheduler_backend
412
+
413
+
414
+ def interactive_component_remove_selection(project_path: Path) -> list[str]:
415
+ """
416
+ Interactive component selection for removing from project.
417
+
418
+ Shows currently enabled components (selectable) and core components
419
+ (grayed out, cannot remove). Displays deletion warnings.
420
+
421
+ Args:
422
+ project_path: Path to the existing project
423
+
424
+ Returns:
425
+ List of components to remove
426
+ """
427
+ from ..core.copier_manager import load_copier_answers
428
+
429
+ # Load current project state
430
+ try:
431
+ current_answers = load_copier_answers(project_path)
432
+ except Exception as e:
433
+ typer.echo(f"āŒ Failed to load project configuration: {e}", err=True)
434
+ raise typer.Exit(1)
435
+
436
+ typer.echo("\nāš ļø Component Removal")
437
+ typer.echo("=" * 40)
438
+ typer.echo("āš ļø WARNING: This will DELETE component files from your project!")
439
+ typer.echo()
440
+
441
+ # Find enabled components
442
+ enabled_removable = []
443
+ for component in ["redis", "worker", "scheduler", "database"]:
444
+ if current_answers.get(f"include_{component}"):
445
+ enabled_removable.append(component)
446
+
447
+ if not enabled_removable:
448
+ typer.echo("ā„¹ļø No optional components to remove")
449
+ typer.echo(" (Core components backend + frontend cannot be removed)")
450
+ return []
451
+
452
+ typer.echo("Currently enabled components:\n")
453
+
454
+ # Show core components (not removable)
455
+ typer.echo(" ⚪ backend - Core component (cannot remove)")
456
+ typer.echo(" ⚪ frontend - Core component (cannot remove)")
457
+ typer.echo()
458
+
459
+ # Show removable components
460
+ selected = []
461
+ for component_name in enabled_removable:
462
+ component_spec = COMPONENTS.get(component_name)
463
+ if component_spec:
464
+ prompt = f" Remove {component_spec.description.lower()}?"
465
+ if typer.confirm(prompt):
466
+ selected.append(component_name)
467
+
468
+ return selected
469
+
470
+
471
+ def interactive_service_selection(project_path: Path) -> list[str]:
472
+ """
473
+ Interactive service selection for adding to existing project.
474
+
475
+ Shows available services with their descriptions and required components.
476
+ Warns if required components are missing.
477
+
478
+ Args:
479
+ project_path: Path to the existing project
480
+
481
+ Returns:
482
+ List of services to add
483
+ """
484
+ from ..core.copier_manager import load_copier_answers
485
+
486
+ # Load current project state
487
+ try:
488
+ current_answers = load_copier_answers(project_path)
489
+ except Exception as e:
490
+ typer.echo(f"āŒ Failed to load project configuration: {e}", err=True)
491
+ raise typer.Exit(1)
492
+
493
+ typer.echo("\nšŸ”§ Service Selection")
494
+ typer.echo("=" * 40)
495
+ typer.echo("Services provide business logic functionality for your application.\n")
496
+
497
+ # Find already enabled services
498
+ enabled_services = []
499
+ for service_name in SERVICES:
500
+ if current_answers.get(f"include_{service_name}"):
501
+ enabled_services.append(service_name)
502
+
503
+ # Find enabled components
504
+ enabled_components = set(CORE_COMPONENTS) # Always have core components
505
+ for component in ["redis", "worker", "scheduler", "database"]:
506
+ if current_answers.get(f"include_{component}"):
507
+ enabled_components.add(component)
508
+
509
+ if enabled_services:
510
+ typer.echo("Currently enabled services:")
511
+ for service_name in enabled_services:
512
+ service_spec = SERVICES[service_name]
513
+ typer.echo(f" āœ… {service_name}: {service_spec.description}")
514
+ typer.echo()
515
+
516
+ # Show available services grouped by type
517
+ selected_services = []
518
+
519
+ # Authentication Services
520
+ auth_services = get_services_by_type(ServiceType.AUTH)
521
+ if auth_services:
522
+ typer.echo("šŸ” Authentication Services:")
523
+ for service_name, service_spec in auth_services.items():
524
+ # Skip if already enabled
525
+ if service_name in enabled_services:
526
+ typer.echo(f" āœ… {service_name} - Already enabled")
527
+ continue
528
+
529
+ # Check component requirements
530
+ missing_components = [
531
+ comp
532
+ for comp in service_spec.required_components
533
+ if comp not in enabled_components
534
+ ]
535
+
536
+ if missing_components:
537
+ requirement_text = f" (will auto-add: {', '.join(missing_components)})"
538
+ else:
539
+ requirement_text = ""
540
+
541
+ prompt = f" Add {service_spec.description.lower()}{requirement_text}?"
542
+ if typer.confirm(prompt):
543
+ selected_services.append(service_name)
544
+
545
+ if missing_components:
546
+ typer.echo(
547
+ f" šŸ“¦ Required components will be added: {', '.join(missing_components)}"
548
+ )
549
+
550
+ # AI & Machine Learning Services
551
+ ai_services = get_services_by_type(ServiceType.AI)
552
+ if ai_services:
553
+ typer.echo("\nšŸ¤– AI & Machine Learning Services:")
554
+ for service_name, service_spec in ai_services.items():
555
+ # Skip if already enabled
556
+ if service_name in enabled_services:
557
+ typer.echo(f" āœ… {service_name} - Already enabled")
558
+ continue
559
+
560
+ # Check component requirements
561
+ missing_components = [
562
+ comp
563
+ for comp in service_spec.required_components
564
+ if comp not in enabled_components
565
+ ]
566
+
567
+ if missing_components:
568
+ requirement_text = f" (will auto-add: {', '.join(missing_components)})"
569
+ else:
570
+ requirement_text = ""
571
+
572
+ prompt = f" Add {service_spec.description.lower()}{requirement_text}?"
573
+ if typer.confirm(prompt):
574
+ selected_services.append(service_name)
575
+
576
+ if missing_components:
577
+ typer.echo(
578
+ f" šŸ“¦ Required components will be added: {', '.join(missing_components)}"
579
+ )
580
+
581
+ # Payment Services (when they exist)
582
+ payment_services = get_services_by_type(ServiceType.PAYMENT)
583
+ if payment_services:
584
+ typer.echo("\nšŸ’° Payment Services:")
585
+ for service_name, service_spec in payment_services.items():
586
+ if service_name in enabled_services:
587
+ typer.echo(f" āœ… {service_name} - Already enabled")
588
+ continue
589
+
590
+ missing_components = [
591
+ comp
592
+ for comp in service_spec.required_components
593
+ if comp not in enabled_components
594
+ ]
595
+
596
+ requirement_text = (
597
+ f" (will auto-add: {', '.join(missing_components)})"
598
+ if missing_components
599
+ else ""
600
+ )
601
+
602
+ prompt = f" Add {service_spec.description.lower()}{requirement_text}?"
603
+ if typer.confirm(prompt):
604
+ selected_services.append(service_name)
605
+
606
+ if missing_components:
607
+ typer.echo(
608
+ f" šŸ“¦ Required components will be added: {', '.join(missing_components)}"
609
+ )
610
+
611
+ return selected_services
aegis/cli/utils.py ADDED
@@ -0,0 +1,70 @@
1
+ """
2
+ CLI utility functions.
3
+
4
+ This module contains utility functions used by CLI commands for
5
+ component detection, dependency expansion, and other common tasks.
6
+ """
7
+
8
+ import typer
9
+
10
+ from ..core.component_utils import (
11
+ clean_component_names,
12
+ extract_base_component_name,
13
+ extract_engine_info,
14
+ )
15
+
16
+
17
+ def detect_scheduler_backend(components: list[str]) -> str:
18
+ """
19
+ Detect scheduler backend from component list.
20
+
21
+ Args:
22
+ components: List of component names, possibly including scheduler[backend]
23
+
24
+ Returns:
25
+ Backend name: "memory", "sqlite", or "postgres"
26
+ """
27
+ for component in components:
28
+ base_name = extract_base_component_name(component)
29
+ if base_name == "scheduler":
30
+ engine = extract_engine_info(component)
31
+ if engine:
32
+ # Direct scheduler[backend] syntax
33
+ return engine
34
+ else:
35
+ # Check if database is also present (legacy detection)
36
+ clean_names = clean_component_names(components)
37
+ if "database" in clean_names:
38
+ return "sqlite" # Default database backend
39
+ return "memory" # Default to memory-only
40
+
41
+
42
+ def expand_scheduler_dependencies(components: list[str]) -> list[str]:
43
+ """
44
+ Expand scheduler[backend] to include required database dependencies.
45
+
46
+ Args:
47
+ components: List of component names
48
+
49
+ Returns:
50
+ Expanded component list with auto-added dependencies
51
+ """
52
+ result = list(components) # Copy the list
53
+
54
+ for component in components:
55
+ base_name = extract_base_component_name(component)
56
+ if base_name == "scheduler":
57
+ backend = extract_engine_info(component)
58
+ if backend and backend != "memory":
59
+ # Auto-add database with same backend if not already present
60
+ database_component = f"database[{backend}]"
61
+ existing_clean = clean_component_names(result)
62
+
63
+ if "database" not in existing_clean:
64
+ result.append(database_component)
65
+ typer.echo(
66
+ f"šŸ“¦ Auto-added database[{backend}] for "
67
+ f"scheduler[{backend}] persistence"
68
+ )
69
+
70
+ return result