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
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,6 @@
1
+ """
2
+ Core modules for Aegis Stack CLI.
3
+
4
+ This package contains the foundational components for the CLI system,
5
+ including component definitions, dependency resolution, and template generation.
6
+ """
@@ -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)