aegis-stack 0.2.0rc2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- aegis/__init__.py +5 -0
- aegis/__main__.py +51 -0
- aegis/cli/__init__.py +6 -0
- aegis/cli/callbacks.py +114 -0
- aegis/cli/interactive.py +611 -0
- aegis/cli/utils.py +70 -0
- aegis/cli/validators.py +34 -0
- aegis/commands/__init__.py +6 -0
- aegis/commands/add.py +353 -0
- aegis/commands/add_service.py +332 -0
- aegis/commands/components.py +35 -0
- aegis/commands/init.py +370 -0
- aegis/commands/remove.py +227 -0
- aegis/commands/services.py +52 -0
- aegis/commands/update.py +252 -0
- aegis/commands/version.py +12 -0
- aegis/config/__init__.py +1 -0
- aegis/config/shared_files.py +136 -0
- aegis/core/CLAUDE.md +377 -0
- aegis/core/__init__.py +6 -0
- aegis/core/component_files.py +228 -0
- aegis/core/component_utils.py +220 -0
- aegis/core/components.py +127 -0
- aegis/core/copier_manager.py +315 -0
- aegis/core/copier_updater.py +475 -0
- aegis/core/dependency_resolver.py +119 -0
- aegis/core/manual_updater.py +554 -0
- aegis/core/post_gen_tasks.py +547 -0
- aegis/core/service_resolver.py +261 -0
- aegis/core/services.py +157 -0
- aegis/core/template_generator.py +266 -0
- aegis/core/version_compatibility.py +259 -0
- aegis/templates/CLAUDE.md +591 -0
- aegis/templates/cookiecutter-aegis-project/cookiecutter.json +39 -0
- aegis/templates/cookiecutter-aegis-project/hooks/post_gen_project.py +214 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/.dockerignore +71 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/.env.example.j2 +130 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/.gitignore +131 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/Dockerfile +53 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/Makefile +236 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/README.md.j2 +196 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/alembic/alembic.ini.j2 +111 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/alembic/env.py.j2 +91 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/alembic/script.py.mako +25 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/alembic/versions/001_initial_auth.py.j2 +51 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/__init__.py +5 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/__init__.py +6 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/ai.py.j2 +700 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/ai_rendering.py +361 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/auth.py.j2 +253 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/health.py.j2 +419 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/load_test.py.j2 +656 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/main.py.j2 +65 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/marko_terminal_renderer.py +489 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/tasks.py.j2 +328 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/{% if cookiecutter.include_scheduler == /"yes/" %}tasks.py{% endif %}" +340 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/__init__.py +0 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/__init__.py +0 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/ai/__init__.py +8 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/ai/router.py +329 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/auth/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/auth/router.py +64 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/deps.py +58 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/health.py +163 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/models.py.j2 +280 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/routing.py.j2 +32 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/scheduler.py.j2 +121 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/worker.py.j2 +478 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/hooks.py +144 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/main.py +31 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/middleware/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/middleware/cors.py +20 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/shutdown/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/shutdown/cleanup.py +14 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/startup/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/startup/component_health.py.j2 +418 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/startup/database_init.py.j2 +83 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/__init__.py +5 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/controls/__init__.py +27 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/controls/table.py +78 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/controls/text.py +142 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/__init__.py.j2 +47 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/ai_card.py +287 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/auth_card.py +198 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/base_card.py +256 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/card_factory.py +227 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/card_utils.py +333 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/database_card.py +420 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/fastapi_card.py +328 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/flet_card.py +267 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/redis_card.py +322 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/scheduler_card.py +352 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/services_card.py +233 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/worker_card.py +684 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/main.py.j2 +653 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/theme.py +48 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/scheduler/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/scheduler/main.py.j2 +156 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/CLAUDE.md.j2 +213 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/__init__.py +6 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/constants.py.j2 +30 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/pools.py +97 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/queues/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/queues/load_test.py +55 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/queues/media.py +49 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/queues/system.py +44 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/registry.py +139 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/tasks/__init__.py +120 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/tasks/load_tasks.py +507 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/tasks/simple_system_tasks.py +33 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/tasks/system_tasks.py +281 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/core/config.py.j2 +178 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/core/constants.py +58 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/core/db.py.j2 +176 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/core/log.py +92 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/core/security.py +62 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/entrypoints/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/entrypoints/webserver.py +40 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/entrypoints/{% if cookiecutter.include_scheduler == /"yes/" %}scheduler.py{% endif %}" +21 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/integrations/__init__.py +0 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/integrations/main.py +62 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/models/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/models/user.py +44 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/py.typed +0 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/__init__.py +8 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/config.py +130 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/conversation.py +213 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/health.py +96 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/models.py +229 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/providers.py +370 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/service.py +388 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/auth/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/auth/auth_service.py +41 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/auth/health.py +164 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/auth/user_service.py +83 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/backend/middleware_inspector.py.j2 +223 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/backend/models.py.j2 +70 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/backend/route_inspector.py.j2 +155 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/load_test.py +679 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/load_test_models.py +266 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/scheduler/__init__.py.j2 +21 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/scheduler/models.py.j2 +119 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/scheduler/scheduled_task_manager.py.j2 +273 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/scheduler/task_monitor.py.j2 +189 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/shared/__init__.py +15 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/shared/models.py +26 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/__init__.py +52 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/alerts.py +94 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/backup.py.j2 +119 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/health.py.j2 +1333 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/models.py +243 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/ui.py +52 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/assets/aegis-manifesto-dark.png +0 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/assets/aegis-manifesto-square-backup.png +0 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/assets/aegis-manifesto.png +0 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/.dockerignore +71 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/.env.example.j2 +64 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/.gitignore +131 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/Dockerfile +53 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/Makefile +211 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/README.md.j2 +172 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/docker-compose.yml.j2 +78 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/mkdocs.yml.j2 +62 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/pyproject.toml.j2 +120 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/uv.lock +1673 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docker-compose.yml.j2 +200 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/api.md +191 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/components/scheduler.md +0 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/components/scheduler.md.j2 +621 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/development.md +215 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/health.md +240 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/javascripts/mermaid-config.js +62 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/stylesheets/mermaid.css +95 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/mkdocs.yml.j2 +62 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/pyproject.toml.j2 +131 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/scripts/entrypoint.sh +87 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/scripts/entrypoint.sh.j2 +93 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/scripts/gen_docs.py +16 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/api/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/api/test_auth_endpoints.py.j2 +307 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/api/test_health_endpoints.py.j2 +262 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/api/test_scheduler_endpoints.py.j2 +214 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/api/test_worker_endpoints.py.j2 +165 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/cli/test_ai_rendering.py +427 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/cli/test_conversation_memory.py +465 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/components/test_scheduler.py +43 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/conftest.py.j2 +195 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/ai/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/ai/conftest.py +78 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/ai/test_health.py +157 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/ai/test_models.py +164 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/ai/test_service.py +198 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_auth_integration.py.j2 +528 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_component_integration.py.j2 +387 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_conversation_persistence.py +342 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_health_logic.py.j2 +663 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_load_test_models.py +619 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_load_test_service.py +603 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_middleware_inspector.py.j2 +248 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_scheduled_task_manager.py.j2 +292 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_system_service.py +98 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_worker_health_registration.py.j2 +257 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/test_core.py +49 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/uv.lock +1673 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/.copier-answers.yml.jinja +21 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/.dockerignore +71 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/.env.example.jinja +130 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/.gitignore +131 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/Dockerfile +53 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/Makefile.jinja +236 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/README.md.jinja +196 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/alembic/alembic.ini.jinja +111 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/alembic/env.py.jinja +91 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/alembic/script.py.mako +25 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/alembic/versions/001_initial_auth.py.jinja +51 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/__init__.py.jinja +5 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/__init__.py.jinja +6 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/ai.py.jinja +700 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/ai_rendering.py +360 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/auth.py.jinja +253 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/health.py.jinja +419 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/load_test.py.jinja +656 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/main.py.jinja +65 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/marko_terminal_renderer.py +489 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/tasks.py.jinja +328 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/__init__.py +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/__init__.py +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/ai/__init__.py +8 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/ai/router.py +329 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/auth/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/auth/router.py +64 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/deps.py +58 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/health.py.jinja +163 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/models.py.jinja +280 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/routing.py.jinja +32 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/scheduler.py.jinja +121 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/worker.py.jinja +478 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/hooks.py +144 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/main.py +31 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/middleware/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/middleware/cors.py +20 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/shutdown/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/shutdown/cleanup.py +14 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/startup/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/startup/component_health.py.jinja +418 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/startup/database_init.py.jinja +83 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/__init__.py +5 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/controls/__init__.py +27 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/controls/table.py +78 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/controls/text.py +142 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/__init__.py.jinja +47 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/ai_card.py +287 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/auth_card.py +198 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/base_card.py +256 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/card_factory.py +227 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/card_utils.py +333 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/database_card.py +420 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/fastapi_card.py +328 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/flet_card.py +267 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/redis_card.py +322 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/scheduler_card.py +352 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/services_card.py +233 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/worker_card.py +684 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/main.py.jinja +653 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/theme.py +48 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/scheduler/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/scheduler/main.py.jinja +156 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/CLAUDE.md.jinja +213 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/__init__.py +6 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/constants.py.jinja +30 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/pools.py +97 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/queues/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/queues/load_test.py +55 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/queues/media.py +49 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/queues/system.py +44 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/registry.py +139 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/tasks/__init__.py +120 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/tasks/load_tasks.py +507 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/tasks/simple_system_tasks.py +33 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/tasks/system_tasks.py +281 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/core/config.py.jinja +178 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/core/constants.py +58 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/core/db.py.jinja +176 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/core/log.py +92 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/core/security.py +62 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/entrypoints/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/entrypoints/scheduler.py.jinja +21 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/entrypoints/webserver.py +39 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/integrations/__init__.py +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/integrations/main.py +61 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/models/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/models/user.py +44 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/py.typed +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/__init__.py +8 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/config.py +130 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/conversation.py +213 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/health.py +96 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/models.py +229 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/providers.py.jinja +370 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/service.py +387 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/auth/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/auth/auth_service.py +40 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/auth/health.py +162 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/auth/user_service.py +82 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/backend/middleware_inspector.py.jinja +223 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/backend/models.py.jinja +70 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/backend/route_inspector.py.jinja +155 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/load_test.py +678 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/load_test_models.py +265 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/scheduler/__init__.py.jinja +21 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/scheduler/models.py.jinja +119 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/scheduler/scheduled_task_manager.py.jinja +273 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/scheduler/task_monitor.py.jinja +189 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/shared/__init__.py +15 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/shared/models.py +26 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/system/__init__.py +52 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/system/alerts.py +94 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/system/backup.py.jinja +119 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/system/health.py.jinja +1333 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/system/models.py +243 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/system/ui.py +52 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57223!aegis-manifesto.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57224!aegis-manifesto-dark.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57225!aegis-manifesto-square-backup.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57533!aegis-manifesto.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57534!aegis-manifesto-dark.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57538!aegis-manifesto-square-backup.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57897!aegis-manifesto.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57898!aegis-manifesto-dark.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57904!aegis-manifesto-square-backup.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!58315!aegis-manifesto.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!58316!aegis-manifesto-dark.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!58324!aegis-manifesto-square-backup.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!58837!aegis-manifesto.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!58838!aegis-manifesto-dark.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!58849!aegis-manifesto-square-backup.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/aegis-manifesto-dark.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/aegis-manifesto-square-backup.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/aegis-manifesto.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/clean-validation/.env.example.jinja +64 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/clean-validation/README.md.jinja +172 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/clean-validation/docker-compose.yml.jinja +78 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/clean-validation/mkdocs.yml.jinja +62 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/clean-validation/pyproject.toml.jinja +120 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/docker-compose.yml.jinja +200 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/docs/api.md.jinja +191 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/docs/components/scheduler.md +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/docs/components/scheduler.md.jinja +621 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/docs/development.md.jinja +215 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/docs/health.md.jinja +240 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/docs/javascripts/mermaid-config.js +62 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/docs/stylesheets/mermaid.css +95 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/mkdocs.yml.jinja +62 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/pyproject.toml.jinja +131 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/scripts/entrypoint.sh +87 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/scripts/entrypoint.sh.jinja +93 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/scripts/gen_docs.py +16 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/api/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/api/test_auth_endpoints.py.jinja +307 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/api/test_health_endpoints.py.jinja +262 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/api/test_scheduler_endpoints.py.jinja +214 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/api/test_worker_endpoints.py.jinja +165 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/cli/test_ai_rendering.py +427 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/cli/test_conversation_memory.py +465 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/components/test_scheduler.py +43 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/conftest.py.jinja +195 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/ai/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/ai/conftest.py +78 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/ai/test_health.py +157 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/ai/test_models.py +164 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/ai/test_service.py +198 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_auth_integration.py.jinja +528 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_component_integration.py.jinja +387 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_conversation_persistence.py +342 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_health_logic.py.jinja +663 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_load_test_models.py +619 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_load_test_service.py +603 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_middleware_inspector.py.jinja +248 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_scheduled_task_manager.py.jinja +292 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_system_service.py +98 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_worker_health_registration.py.jinja +257 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/test_core.py +49 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/uv.lock +1673 -0
- aegis_stack-0.2.0rc2.dist-info/METADATA +165 -0
- aegis_stack-0.2.0rc2.dist-info/RECORD +392 -0
- aegis_stack-0.2.0rc2.dist-info/WHEEL +4 -0
- aegis_stack-0.2.0rc2.dist-info/entry_points.txt +3 -0
- aegis_stack-0.2.0rc2.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Main CLI application entry point.
|
|
3
|
+
|
|
4
|
+
Command-line interface for {{ project_slug }} management tasks.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import importlib
|
|
8
|
+
from typing import TYPE_CHECKING
|
|
9
|
+
|
|
10
|
+
import typer
|
|
11
|
+
|
|
12
|
+
from app.cli import health
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
pass
|
|
16
|
+
|
|
17
|
+
app = typer.Typer(
|
|
18
|
+
name="{{ project_slug }}",
|
|
19
|
+
help="{{ project_slug }} management CLI",
|
|
20
|
+
no_args_is_help=True,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
# Register sub-commands
|
|
24
|
+
app.add_typer(health.app, name="health")
|
|
25
|
+
|
|
26
|
+
# Conditionally register load-test command if worker components are available
|
|
27
|
+
try:
|
|
28
|
+
load_test_module = importlib.import_module("app.cli.load_test")
|
|
29
|
+
app.add_typer(load_test_module.app, name="load-test")
|
|
30
|
+
except ImportError:
|
|
31
|
+
# Worker components not available, skip load-test commands
|
|
32
|
+
pass
|
|
33
|
+
|
|
34
|
+
# Conditionally register tasks command if scheduler components are available
|
|
35
|
+
try:
|
|
36
|
+
tasks_module = importlib.import_module("app.cli.tasks")
|
|
37
|
+
app.add_typer(tasks_module.app, name="tasks")
|
|
38
|
+
except (ImportError, AttributeError):
|
|
39
|
+
# Scheduler components not available or tasks module incomplete, skip tasks commands
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
# Conditionally register auth command if auth service is available
|
|
43
|
+
try:
|
|
44
|
+
auth_module = importlib.import_module("app.cli.auth")
|
|
45
|
+
app.add_typer(auth_module.app, name="auth")
|
|
46
|
+
except ImportError:
|
|
47
|
+
# Auth service not available, skip auth commands
|
|
48
|
+
pass
|
|
49
|
+
|
|
50
|
+
# Conditionally register ai command if ai service is available
|
|
51
|
+
try:
|
|
52
|
+
ai_module = importlib.import_module("app.cli.ai")
|
|
53
|
+
app.add_typer(ai_module.app, name="ai")
|
|
54
|
+
except ImportError:
|
|
55
|
+
# AI service not available, skip ai commands
|
|
56
|
+
pass
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def main() -> None:
|
|
60
|
+
"""Entry point for the CLI application."""
|
|
61
|
+
app()
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
if __name__ == "__main__":
|
|
65
|
+
main()
|
|
@@ -0,0 +1,489 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Custom terminal renderer for marko markdown parser.
|
|
3
|
+
|
|
4
|
+
Renders markdown elements as ANSI-styled strings for beautiful terminal output.
|
|
5
|
+
Integrates with Rich Console for output management while keeping rendering simple.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from marko.renderer import Renderer
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class TerminalRenderer(Renderer):
|
|
12
|
+
"""
|
|
13
|
+
Terminal renderer that outputs ANSI-styled strings.
|
|
14
|
+
|
|
15
|
+
Renders markdown elements with ANSI escape codes for colored,
|
|
16
|
+
styled terminal output. Works with Rich Console for output but
|
|
17
|
+
doesn't depend on Rich components.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def _get_content(self, element) -> str:
|
|
21
|
+
"""
|
|
22
|
+
Safely get content from element, handling both string and list children.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
element: Element with children attribute
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
String content
|
|
29
|
+
"""
|
|
30
|
+
# If children is already a string, return it
|
|
31
|
+
if isinstance(element.children, str):
|
|
32
|
+
return element.children
|
|
33
|
+
# Otherwise render children as normal
|
|
34
|
+
return self.render_children(element)
|
|
35
|
+
|
|
36
|
+
def render_document(self, element) -> str:
|
|
37
|
+
"""Render the root document element."""
|
|
38
|
+
return self.render_children(element)
|
|
39
|
+
|
|
40
|
+
def render_heading(self, element) -> str:
|
|
41
|
+
"""
|
|
42
|
+
Render heading with ANSI color codes based on level.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
element: Heading element with level attribute
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
ANSI-styled heading string
|
|
49
|
+
"""
|
|
50
|
+
# Extract heading level (h1=1, h2=2, h3=3, etc.)
|
|
51
|
+
level = int(element.level)
|
|
52
|
+
|
|
53
|
+
# Render children to get heading text
|
|
54
|
+
content = self.render_children(element)
|
|
55
|
+
|
|
56
|
+
# Color codes by level
|
|
57
|
+
colors = {
|
|
58
|
+
1: "\033[1;32m", # Bold green
|
|
59
|
+
2: "\033[1;36m", # Bold cyan
|
|
60
|
+
3: "\033[1;34m", # Bold blue
|
|
61
|
+
}
|
|
62
|
+
reset = "\033[0m"
|
|
63
|
+
|
|
64
|
+
# Get color for this level (default to bold white)
|
|
65
|
+
color = colors.get(level, "\033[1;37m")
|
|
66
|
+
|
|
67
|
+
# Clean title rendering (no ### prefix)
|
|
68
|
+
return f"{color}{content}{reset}\n"
|
|
69
|
+
|
|
70
|
+
def render_fenced_code(self, element) -> str:
|
|
71
|
+
"""
|
|
72
|
+
Render fenced code block with syntax highlighting.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
element: Fenced code element with lang and children
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
Code block with syntax highlighting (no background)
|
|
79
|
+
"""
|
|
80
|
+
# Get code content from children (safely handles string or list)
|
|
81
|
+
code = self._get_content(element)
|
|
82
|
+
lang = getattr(element, "lang", "") or "text"
|
|
83
|
+
|
|
84
|
+
if not code or not code.strip():
|
|
85
|
+
return ""
|
|
86
|
+
|
|
87
|
+
# Try to use pygments for syntax highlighting if available
|
|
88
|
+
try:
|
|
89
|
+
from pygments import highlight
|
|
90
|
+
from pygments.formatters import Terminal256Formatter
|
|
91
|
+
from pygments.lexers import get_lexer_by_name
|
|
92
|
+
from pygments.util import ClassNotFound
|
|
93
|
+
|
|
94
|
+
try:
|
|
95
|
+
lexer = get_lexer_by_name(lang, stripall=True)
|
|
96
|
+
formatter = Terminal256Formatter(style="monokai")
|
|
97
|
+
highlighted = highlight(code, lexer, formatter)
|
|
98
|
+
|
|
99
|
+
# Process each line: add indentation only
|
|
100
|
+
lines = highlighted.rstrip("\n").split("\n")
|
|
101
|
+
formatted_lines = []
|
|
102
|
+
for line in lines:
|
|
103
|
+
# Add 4-space indentation
|
|
104
|
+
formatted_lines.append(f" {line}")
|
|
105
|
+
|
|
106
|
+
result = "\n".join(formatted_lines)
|
|
107
|
+
# Add blank lines before/after
|
|
108
|
+
return f"\n{result}\n\n"
|
|
109
|
+
|
|
110
|
+
except ClassNotFound:
|
|
111
|
+
# Lexer not found, fall back to plain display
|
|
112
|
+
pass
|
|
113
|
+
|
|
114
|
+
except ImportError:
|
|
115
|
+
# Pygments not available, use plain display
|
|
116
|
+
pass
|
|
117
|
+
|
|
118
|
+
# Fallback: plain code with indentation
|
|
119
|
+
lines = code.strip().split("\n")
|
|
120
|
+
formatted_lines = []
|
|
121
|
+
for line in lines:
|
|
122
|
+
formatted_lines.append(f" {line}")
|
|
123
|
+
|
|
124
|
+
result = "\n".join(formatted_lines)
|
|
125
|
+
return f"\n{result}\n\n"
|
|
126
|
+
|
|
127
|
+
def render_code_block(self, element) -> str:
|
|
128
|
+
"""Render indented code block with simple indentation."""
|
|
129
|
+
code = self._get_content(element)
|
|
130
|
+
|
|
131
|
+
if not code or not code.strip():
|
|
132
|
+
return ""
|
|
133
|
+
|
|
134
|
+
# Simple indented code (no background)
|
|
135
|
+
lines = code.strip().split("\n")
|
|
136
|
+
formatted_lines = []
|
|
137
|
+
for line in lines:
|
|
138
|
+
formatted_lines.append(f" {line}")
|
|
139
|
+
|
|
140
|
+
result = "\n".join(formatted_lines)
|
|
141
|
+
return f"\n{result}\n\n"
|
|
142
|
+
|
|
143
|
+
def render_list(self, element) -> str:
|
|
144
|
+
"""Render bullet or ordered list."""
|
|
145
|
+
return self.render_children(element)
|
|
146
|
+
|
|
147
|
+
def render_list_item(self, element) -> str:
|
|
148
|
+
"""
|
|
149
|
+
Render list item with styled bullet.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
element: List item element
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
Styled list item string
|
|
156
|
+
"""
|
|
157
|
+
content = self.render_children(element).strip()
|
|
158
|
+
|
|
159
|
+
# Yellow bullet with proper indentation
|
|
160
|
+
bullet_color = "\033[93m" # Bright yellow
|
|
161
|
+
reset = "\033[0m"
|
|
162
|
+
|
|
163
|
+
return f" {bullet_color}•{reset} {content}\n"
|
|
164
|
+
|
|
165
|
+
def render_paragraph(self, element) -> str:
|
|
166
|
+
"""
|
|
167
|
+
Render paragraph as plain text.
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
element: Paragraph element
|
|
171
|
+
|
|
172
|
+
Returns:
|
|
173
|
+
Paragraph text with newline
|
|
174
|
+
"""
|
|
175
|
+
content = self.render_children(element)
|
|
176
|
+
return f"{content}\n"
|
|
177
|
+
|
|
178
|
+
def render_blank_line(self, element) -> str:
|
|
179
|
+
"""
|
|
180
|
+
Render blank line.
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
element: Blank line element
|
|
184
|
+
|
|
185
|
+
Returns:
|
|
186
|
+
Single newline
|
|
187
|
+
"""
|
|
188
|
+
return "\n"
|
|
189
|
+
|
|
190
|
+
def render_emphasis(self, element) -> str:
|
|
191
|
+
"""
|
|
192
|
+
Render italic text with ANSI codes.
|
|
193
|
+
|
|
194
|
+
Args:
|
|
195
|
+
element: Emphasis element
|
|
196
|
+
|
|
197
|
+
Returns:
|
|
198
|
+
Italic styled string
|
|
199
|
+
"""
|
|
200
|
+
content = self.render_children(element)
|
|
201
|
+
return f"\033[3m{content}\033[0m" # Italic
|
|
202
|
+
|
|
203
|
+
def render_strong_emphasis(self, element) -> str:
|
|
204
|
+
"""
|
|
205
|
+
Render bold text with ANSI codes.
|
|
206
|
+
|
|
207
|
+
Args:
|
|
208
|
+
element: Strong emphasis element
|
|
209
|
+
|
|
210
|
+
Returns:
|
|
211
|
+
Bold styled string
|
|
212
|
+
"""
|
|
213
|
+
content = self.render_children(element)
|
|
214
|
+
return f"\033[1m{content}\033[0m" # Bold
|
|
215
|
+
|
|
216
|
+
def render_code_span(self, element) -> str:
|
|
217
|
+
"""
|
|
218
|
+
Render inline code with background color.
|
|
219
|
+
|
|
220
|
+
Args:
|
|
221
|
+
element: Inline code element
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
Styled inline code string
|
|
225
|
+
"""
|
|
226
|
+
content = self._get_content(element)
|
|
227
|
+
# Bright magenta on black background
|
|
228
|
+
return f"\033[95;40m{content}\033[0m"
|
|
229
|
+
|
|
230
|
+
def render_raw_text(self, element) -> str:
|
|
231
|
+
"""
|
|
232
|
+
Render raw text content.
|
|
233
|
+
|
|
234
|
+
Args:
|
|
235
|
+
element: Raw text element
|
|
236
|
+
|
|
237
|
+
Returns:
|
|
238
|
+
Plain text string
|
|
239
|
+
"""
|
|
240
|
+
# RawText.children is already a string, not a list
|
|
241
|
+
# Just return it directly
|
|
242
|
+
if isinstance(element.children, str):
|
|
243
|
+
return element.children
|
|
244
|
+
# Fallback for safety
|
|
245
|
+
return str(element.children)
|
|
246
|
+
|
|
247
|
+
def render_line_break(self, element) -> str:
|
|
248
|
+
"""Render line break."""
|
|
249
|
+
return "\n"
|
|
250
|
+
|
|
251
|
+
def render_link(self, element) -> str:
|
|
252
|
+
"""
|
|
253
|
+
Render hyperlink.
|
|
254
|
+
|
|
255
|
+
Args:
|
|
256
|
+
element: Link element
|
|
257
|
+
|
|
258
|
+
Returns:
|
|
259
|
+
Link text (URLs hidden in terminal)
|
|
260
|
+
"""
|
|
261
|
+
content = self.render_children(element)
|
|
262
|
+
# Just show the link text, not the URL (terminal limitation)
|
|
263
|
+
return content
|
|
264
|
+
|
|
265
|
+
def render_image(self, element) -> str:
|
|
266
|
+
"""
|
|
267
|
+
Render image placeholder.
|
|
268
|
+
|
|
269
|
+
Args:
|
|
270
|
+
element: Image element
|
|
271
|
+
|
|
272
|
+
Returns:
|
|
273
|
+
Image placeholder text
|
|
274
|
+
"""
|
|
275
|
+
title = getattr(element, "title", "") or "image"
|
|
276
|
+
return f"🖼️ [{title}]"
|
|
277
|
+
|
|
278
|
+
def render_thematic_break(self, element) -> str:
|
|
279
|
+
"""Render horizontal rule."""
|
|
280
|
+
return f"\033[2m{'─' * 60}\033[0m\n"
|
|
281
|
+
|
|
282
|
+
def render_block_quote(self, element) -> str:
|
|
283
|
+
"""
|
|
284
|
+
Render block quote with left border.
|
|
285
|
+
|
|
286
|
+
Args:
|
|
287
|
+
element: Block quote element
|
|
288
|
+
|
|
289
|
+
Returns:
|
|
290
|
+
Quoted text with border
|
|
291
|
+
"""
|
|
292
|
+
content = self.render_children(element)
|
|
293
|
+
lines = content.strip().split("\n")
|
|
294
|
+
|
|
295
|
+
border = "\033[36m│\033[0m" # Cyan border
|
|
296
|
+
result = []
|
|
297
|
+
for line in lines:
|
|
298
|
+
result.append(f"{border} {line}")
|
|
299
|
+
|
|
300
|
+
return "\n".join(result) + "\n"
|
|
301
|
+
|
|
302
|
+
def render_table(self, element) -> str:
|
|
303
|
+
"""
|
|
304
|
+
Render table with aligned columns using two-pass approach.
|
|
305
|
+
|
|
306
|
+
Args:
|
|
307
|
+
element: Table element from GFM
|
|
308
|
+
|
|
309
|
+
Returns:
|
|
310
|
+
Formatted table string
|
|
311
|
+
"""
|
|
312
|
+
rows = element.children
|
|
313
|
+
if not rows:
|
|
314
|
+
return ""
|
|
315
|
+
|
|
316
|
+
# First pass: render all cells and collect metadata
|
|
317
|
+
rendered_rows = []
|
|
318
|
+
for row in rows:
|
|
319
|
+
rendered_cells = []
|
|
320
|
+
for cell in row.children:
|
|
321
|
+
content = self.render_children(cell)
|
|
322
|
+
is_header = cell.header
|
|
323
|
+
rendered_cells.append((content, is_header))
|
|
324
|
+
rendered_rows.append(rendered_cells)
|
|
325
|
+
|
|
326
|
+
# Calculate column widths from rendered content
|
|
327
|
+
col_widths = self._calculate_widths_from_rendered(rendered_rows)
|
|
328
|
+
|
|
329
|
+
# Second pass: format rows with alignment and styling
|
|
330
|
+
result = []
|
|
331
|
+
for i, rendered_cells in enumerate(rendered_rows):
|
|
332
|
+
row_parts = []
|
|
333
|
+
for j, (content, is_header) in enumerate(rendered_cells):
|
|
334
|
+
# Strip ANSI for padding calculation
|
|
335
|
+
clean = self._strip_ansi(content)
|
|
336
|
+
padding = col_widths[j] - len(clean)
|
|
337
|
+
|
|
338
|
+
# Apply header styling
|
|
339
|
+
styled = f"\033[1;36m{content}\033[0m" if is_header else content
|
|
340
|
+
|
|
341
|
+
padded = styled + (" " * padding)
|
|
342
|
+
row_parts.append(padded)
|
|
343
|
+
|
|
344
|
+
row_text = "| " + " | ".join(row_parts) + " |"
|
|
345
|
+
result.append(row_text)
|
|
346
|
+
|
|
347
|
+
# Add separator after header row
|
|
348
|
+
if i == 0:
|
|
349
|
+
separator = self._render_table_separator(col_widths)
|
|
350
|
+
result.append(separator)
|
|
351
|
+
|
|
352
|
+
return "\n" + "\n".join(result) + "\n\n"
|
|
353
|
+
|
|
354
|
+
def render_table_row(self, element) -> str:
|
|
355
|
+
"""
|
|
356
|
+
Render table row - return empty as table handles rendering.
|
|
357
|
+
|
|
358
|
+
Args:
|
|
359
|
+
element: TableRow element
|
|
360
|
+
|
|
361
|
+
Returns:
|
|
362
|
+
Empty string (rendering handled by render_table)
|
|
363
|
+
"""
|
|
364
|
+
# Table rendering is handled entirely by render_table
|
|
365
|
+
# This method must exist for marko but returns empty
|
|
366
|
+
return ""
|
|
367
|
+
|
|
368
|
+
def render_table_cell(self, element) -> str:
|
|
369
|
+
"""
|
|
370
|
+
Render cell content without styling.
|
|
371
|
+
|
|
372
|
+
Args:
|
|
373
|
+
element: TableCell element
|
|
374
|
+
|
|
375
|
+
Returns:
|
|
376
|
+
Plain cell content (styling applied by render_table)
|
|
377
|
+
"""
|
|
378
|
+
# Just render children (safely), styling happens in render_table
|
|
379
|
+
return self._get_content(element)
|
|
380
|
+
|
|
381
|
+
def _calculate_widths_from_rendered(
|
|
382
|
+
self, rendered_rows: list[list[tuple[str, bool]]]
|
|
383
|
+
) -> list[int]:
|
|
384
|
+
"""
|
|
385
|
+
Calculate column widths from pre-rendered cells.
|
|
386
|
+
|
|
387
|
+
Args:
|
|
388
|
+
rendered_rows: List of rows, each row is list of (content, is_header)
|
|
389
|
+
|
|
390
|
+
Returns:
|
|
391
|
+
List of column widths
|
|
392
|
+
"""
|
|
393
|
+
col_widths = []
|
|
394
|
+
max_cols = max(len(row) for row in rendered_rows)
|
|
395
|
+
|
|
396
|
+
for col_idx in range(max_cols):
|
|
397
|
+
max_width = 0
|
|
398
|
+
for row in rendered_rows:
|
|
399
|
+
if col_idx < len(row):
|
|
400
|
+
content, _ = row[col_idx]
|
|
401
|
+
clean = self._strip_ansi(content)
|
|
402
|
+
max_width = max(max_width, len(clean))
|
|
403
|
+
col_widths.append(max_width)
|
|
404
|
+
|
|
405
|
+
return col_widths
|
|
406
|
+
|
|
407
|
+
def _strip_ansi(self, text: str) -> str:
|
|
408
|
+
"""
|
|
409
|
+
Strip ANSI escape codes for width calculation.
|
|
410
|
+
|
|
411
|
+
Args:
|
|
412
|
+
text: Text with ANSI codes
|
|
413
|
+
|
|
414
|
+
Returns:
|
|
415
|
+
Clean text without ANSI codes
|
|
416
|
+
"""
|
|
417
|
+
import re
|
|
418
|
+
|
|
419
|
+
ansi_pattern = re.compile(r"\x1b\[[0-9;]*m")
|
|
420
|
+
return ansi_pattern.sub("", text)
|
|
421
|
+
|
|
422
|
+
def _render_table_separator(self, col_widths: list[int]) -> str:
|
|
423
|
+
"""
|
|
424
|
+
Render separator line between header and body.
|
|
425
|
+
|
|
426
|
+
Args:
|
|
427
|
+
col_widths: List of column widths
|
|
428
|
+
|
|
429
|
+
Returns:
|
|
430
|
+
Separator line string
|
|
431
|
+
"""
|
|
432
|
+
segments = ["-" * (width + 2) for width in col_widths]
|
|
433
|
+
return "|" + "|".join(segments) + "|"
|
|
434
|
+
|
|
435
|
+
def render_strikethrough(self, element) -> str:
|
|
436
|
+
"""
|
|
437
|
+
Render strikethrough text (~~text~~) with dim styling.
|
|
438
|
+
|
|
439
|
+
Args:
|
|
440
|
+
element: Strikethrough element from GFM extension
|
|
441
|
+
|
|
442
|
+
Returns:
|
|
443
|
+
Dimmed/faint text string
|
|
444
|
+
"""
|
|
445
|
+
content = self._get_content(element)
|
|
446
|
+
return f"\033[2m{content}\033[0m" # Dim/faint text
|
|
447
|
+
|
|
448
|
+
def render_url(self, element) -> str:
|
|
449
|
+
"""
|
|
450
|
+
Render autolinked URLs.
|
|
451
|
+
|
|
452
|
+
Args:
|
|
453
|
+
element: URL element from GFM extension
|
|
454
|
+
|
|
455
|
+
Returns:
|
|
456
|
+
Cyan-colored URL string
|
|
457
|
+
"""
|
|
458
|
+
url = element.dest if hasattr(element, "dest") else str(element.children)
|
|
459
|
+
return f"\033[36m{url}\033[0m" # Cyan color
|
|
460
|
+
|
|
461
|
+
def __getattr__(self, name: str):
|
|
462
|
+
"""
|
|
463
|
+
Fallback for any missing render methods.
|
|
464
|
+
|
|
465
|
+
Prevents crashes from unknown GFM elements by providing a generic
|
|
466
|
+
renderer that outputs plain text. This ensures compatibility with
|
|
467
|
+
future GFM extensions or elements we haven't explicitly handled.
|
|
468
|
+
|
|
469
|
+
Args:
|
|
470
|
+
name: Attribute name being accessed
|
|
471
|
+
|
|
472
|
+
Returns:
|
|
473
|
+
Fallback render function for render_* methods
|
|
474
|
+
|
|
475
|
+
Raises:
|
|
476
|
+
AttributeError: If attribute is not a render method
|
|
477
|
+
"""
|
|
478
|
+
if name.startswith("render_"):
|
|
479
|
+
|
|
480
|
+
def fallback_render(element):
|
|
481
|
+
"""Generic fallback - renders as plain text."""
|
|
482
|
+
if hasattr(element, "children"):
|
|
483
|
+
return self.render_children(element)
|
|
484
|
+
return str(element)
|
|
485
|
+
|
|
486
|
+
return fallback_render
|
|
487
|
+
raise AttributeError(
|
|
488
|
+
f"'{type(self).__name__}' object has no attribute '{name}'"
|
|
489
|
+
)
|