ntc-dev-kit 1.1.0__tar.gz → 1.2.1__tar.gz

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 (207) hide show
  1. {ntc_dev_kit-1.1.0/ntc_dev_kit.egg-info → ntc_dev_kit-1.2.1}/PKG-INFO +26 -2
  2. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/README.md +25 -1
  3. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/__init__.py +1 -1
  4. ntc_dev_kit-1.2.1/ntc/cli_enhanced.py +421 -0
  5. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/main.py +2 -2
  6. ntc_dev_kit-1.2.1/ntc/templates/auth_app/migrations/0001_initial.py +51 -0
  7. ntc_dev_kit-1.2.1/ntc/templates/auth_app/models.py +54 -0
  8. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/auth_app/serializers.py +8 -1
  9. ntc_dev_kit-1.2.1/ntc/templates/auth_app/tasks.py +28 -0
  10. ntc_dev_kit-1.2.1/ntc/templates/auth_app/templates/authentication/password_reset.html +5 -0
  11. ntc_dev_kit-1.2.1/ntc/templates/auth_app/templates/authentication/password_reset.txt +9 -0
  12. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/auth_app/tests.py +51 -0
  13. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/auth_app/views.py +25 -11
  14. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/main.py +24 -20
  15. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/structure.json +26 -1
  16. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/.entry/start_tasks_backend.sh +2 -1
  17. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/CLAUDE.md +19 -1
  18. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/apps/common_apps.py +3 -0
  19. ntc_dev_kit-1.2.1/ntc/templates/drf_structure/templates/apps/common_checks.py +17 -0
  20. ntc_dev_kit-1.2.1/ntc/templates/drf_structure/templates/ci/github_ci.yml +28 -0
  21. ntc_dev_kit-1.2.1/ntc/templates/drf_structure/templates/ci/pre_commit.yaml +7 -0
  22. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/core/exceptions/handler.py +5 -8
  23. ntc_dev_kit-1.2.1/ntc/templates/drf_structure/templates/core/health.py +68 -0
  24. ntc_dev_kit-1.2.1/ntc/templates/drf_structure/templates/core/renderers.py +15 -0
  25. ntc_dev_kit-1.2.1/ntc/templates/drf_structure/templates/core/response_wrapper.py +27 -0
  26. ntc_dev_kit-1.2.1/ntc/templates/drf_structure/templates/core/schema.py +39 -0
  27. ntc_dev_kit-1.2.1/ntc/templates/drf_structure/templates/core/tests.py +60 -0
  28. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/django_project/django_settings.txt +50 -6
  29. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/django_project/prod_settings.py +5 -0
  30. ntc_dev_kit-1.2.1/ntc/templates/drf_structure/templates/django_project/schema.py +19 -0
  31. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/django_project/test_settings.py +2 -0
  32. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/django_project/urls.py +4 -1
  33. ntc_dev_kit-1.2.1/ntc/templates/drf_structure/templates/docker/docker-compose.yml +65 -0
  34. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/knowledge/knowledge_description.md +1 -1
  35. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/knowledge/knowledge_design_pattern.md +12 -19
  36. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/root/README.md +13 -11
  37. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/root/django_requirements.txt +1 -0
  38. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/src/services/api.service.ts +5 -5
  39. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/src/services/base.service.ts +1 -1
  40. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/utils/output.py +10 -0
  41. ntc_dev_kit-1.2.1/ntc/utils/prompts.py +280 -0
  42. ntc_dev_kit-1.2.1/ntc/wizard.py +320 -0
  43. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1/ntc_dev_kit.egg-info}/PKG-INFO +26 -2
  44. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc_dev_kit.egg-info/SOURCES.txt +13 -0
  45. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/pyproject.toml +1 -1
  46. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/setup.py +1 -1
  47. ntc_dev_kit-1.1.0/ntc/cli_enhanced.py +0 -934
  48. ntc_dev_kit-1.1.0/ntc/templates/auth_app/models.py +0 -30
  49. ntc_dev_kit-1.1.0/ntc/templates/drf_structure/templates/core/response_wrapper.py +0 -12
  50. ntc_dev_kit-1.1.0/ntc/templates/drf_structure/templates/django_project/schema.py +0 -8
  51. ntc_dev_kit-1.1.0/ntc/templates/drf_structure/templates/docker/docker-compose.yml +0 -11
  52. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/LICENSE +0 -0
  53. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/MANIFEST.in +0 -0
  54. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/app_creator.py +0 -0
  55. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/cli.py +0 -0
  56. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/composer/__init__.py +0 -0
  57. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/composer/main.py +0 -0
  58. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/deployment/__init__.py +0 -0
  59. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/__init__.py +0 -0
  60. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/auth_app/__init__.py +0 -0
  61. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/auth_app/admin.py +0 -0
  62. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/auth_app/apps.py +0 -0
  63. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/auth_app/urls.py +0 -0
  64. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/__init__.py +0 -0
  65. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/drf_structure.md +0 -0
  66. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/.entry/start_core_backend.sh +0 -0
  67. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/README.md +0 -0
  68. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/apps/app_admin.py +0 -0
  69. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/apps/app_apps.py +0 -0
  70. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/apps/app_models.py +0 -0
  71. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/apps/app_urls.py +0 -0
  72. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/apps/app_views.py +0 -0
  73. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/apps/common_models.py +0 -0
  74. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/config/gunicorn_config.py +0 -0
  75. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/config/http.conf +0 -0
  76. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/core/formatter.py +0 -0
  77. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/core/logger.py +0 -0
  78. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/core/pagination.py +0 -0
  79. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/core/services/azure/email/backend.py +0 -0
  80. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/core/services/azure/email/client.py +0 -0
  81. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/core/services/azure/email/notifier.py +0 -0
  82. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/core/services/object_storage.py +0 -0
  83. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/core/utils/request.py +0 -0
  84. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/django_project/asgi.py +0 -0
  85. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/django_project/celery.py +0 -0
  86. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/django_project/dev_settings.py +0 -0
  87. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/django_project/project_init.py +0 -0
  88. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/django_project/wsgi.py +0 -0
  89. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/docker/Dockerfile +0 -0
  90. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/docker/captain-definition +0 -0
  91. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/knowledge/knowledge_structure.md +0 -0
  92. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/root/django_env.txt +0 -0
  93. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/root/django_requirements_dev.txt +0 -0
  94. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/root/dockerignore.txt +0 -0
  95. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/root/gitattributes.txt +0 -0
  96. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/root/gitignore.txt +0 -0
  97. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/root/manage.py +0 -0
  98. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/root/pytest_ini.txt +0 -0
  99. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/root/ruff_toml.txt +0 -0
  100. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/flutter/.gitignore +0 -0
  101. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/flutter/.knowledge/knowledge_description.md +0 -0
  102. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/flutter/.knowledge/knowledge_design_pattern.md +0 -0
  103. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/flutter/.knowledge/knowledge_structure.md +0 -0
  104. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/flutter/CLAUDE.md +0 -0
  105. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/flutter/__init__.py +0 -0
  106. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/flutter/analysis_options.yaml +0 -0
  107. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/flutter/lib/core/config/app_config.dart +0 -0
  108. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/flutter/lib/core/config/environment.dart +0 -0
  109. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/flutter/lib/core/constants/app_constants.dart +0 -0
  110. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/flutter/lib/core/network/dio_client.dart +0 -0
  111. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/flutter/lib/features/home/presentation/home_screen.dart +0 -0
  112. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/flutter/lib/l10n/app_localizations.dart +0 -0
  113. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/flutter/lib/main.dart +0 -0
  114. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/flutter/lib/services/api_service.dart +0 -0
  115. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/flutter/lib/shared/localization/locale_provider.dart +0 -0
  116. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/flutter/lib/shared/theme/app_theme.dart +0 -0
  117. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/flutter/lib/shared/theme/theme_provider.dart +0 -0
  118. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/flutter/main.py +0 -0
  119. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/flutter/pubspec.yaml +0 -0
  120. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/flutter/structure.json +0 -0
  121. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/nextjs/.env.example +0 -0
  122. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/nextjs/.gitignore +0 -0
  123. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/nextjs/CLAUDE.md +0 -0
  124. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/nextjs/README.md +0 -0
  125. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/nextjs/__init__.py +0 -0
  126. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/nextjs/eslint.config.mjs +0 -0
  127. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/nextjs/knowledge/knowledge_description.md +0 -0
  128. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/nextjs/knowledge/knowledge_design_pattern.md +0 -0
  129. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/nextjs/knowledge/knowledge_structure.md +0 -0
  130. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/nextjs/main.py +0 -0
  131. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/nextjs/next.config.js +0 -0
  132. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/nextjs/package.json +0 -0
  133. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/nextjs/src/app/globals.css +0 -0
  134. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/nextjs/src/app/layout.tsx +0 -0
  135. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/nextjs/src/app/page.tsx +0 -0
  136. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/nextjs/src/components/Header.tsx +0 -0
  137. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/nextjs/src/services/api.ts +0 -0
  138. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/nextjs/src/services/server.ts +0 -0
  139. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/nextjs/src/utils/constants.ts +0 -0
  140. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/nextjs/structure.json +0 -0
  141. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/nextjs/tsconfig.json +0 -0
  142. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/.env.example +0 -0
  143. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/.gitignore +0 -0
  144. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/CLAUDE.md +0 -0
  145. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/README.md +0 -0
  146. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/__init__.py +0 -0
  147. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/eslint.config.js +0 -0
  148. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/index.html +0 -0
  149. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/knowledge/knowledge_description.md +0 -0
  150. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/knowledge/knowledge_design_pattern.md +0 -0
  151. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/knowledge/knowledge_structure.md +0 -0
  152. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/main.py +0 -0
  153. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/package.json +0 -0
  154. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/src/App.tsx +0 -0
  155. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/src/components/ProtectedRoute.tsx +0 -0
  156. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/src/components/Sidebar.tsx +0 -0
  157. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/src/components/TopNav.tsx +0 -0
  158. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/src/context/AuthContext.tsx +0 -0
  159. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/src/context/ThemeContext.tsx +0 -0
  160. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/src/context/WorkspaceContext.tsx +0 -0
  161. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/src/hooks/useApi.ts +0 -0
  162. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/src/index.css +0 -0
  163. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/src/layouts/AuthLayout.tsx +0 -0
  164. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/src/layouts/DashboardLayout.tsx +0 -0
  165. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/src/main.tsx +0 -0
  166. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/src/pages/DashboardPage.tsx +0 -0
  167. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/src/pages/auth/LoginPage.tsx +0 -0
  168. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/src/services/token.ts +0 -0
  169. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/src/vite-env.d.ts +0 -0
  170. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/structure.json +0 -0
  171. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/tsconfig.app.json +0 -0
  172. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/tsconfig.json +0 -0
  173. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/tsconfig.node.json +0 -0
  174. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/vite.config.ts +0 -0
  175. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_structure/App.css +0 -0
  176. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_structure/CLAUDE.md +0 -0
  177. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_structure/__init__.py +0 -0
  178. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_structure/index.css +0 -0
  179. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_structure/index.html +0 -0
  180. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_structure/knowledge/knowledge_description.md +0 -0
  181. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_structure/knowledge/knowledge_design_pattern.md +0 -0
  182. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_structure/knowledge/knowledge_structure.md +0 -0
  183. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_structure/main.py +0 -0
  184. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_structure/package.json +0 -0
  185. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_structure/src/App.tsx +0 -0
  186. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_structure/src/components/Header.tsx +0 -0
  187. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_structure/src/main.tsx +0 -0
  188. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_structure/src/services/api.ts +0 -0
  189. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_structure/src/utils/constants.ts +0 -0
  190. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_structure/src/vite-env.d.ts +0 -0
  191. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_structure/structure.json +0 -0
  192. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_structure/tsconfig.app.json +0 -0
  193. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_structure/tsconfig.json +0 -0
  194. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_structure/tsconfig.node.json +0 -0
  195. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_structure/vite.config.ts +0 -0
  196. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/ui/__init__.py +0 -0
  197. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/ui/app.py +0 -0
  198. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/utils/__init__.py +0 -0
  199. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/utils/environment.py +0 -0
  200. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/utils/shell.py +0 -0
  201. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/utils/validation.py +0 -0
  202. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc_dev_kit.egg-info/dependency_links.txt +0 -0
  203. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc_dev_kit.egg-info/entry_points.txt +0 -0
  204. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc_dev_kit.egg-info/requires.txt +0 -0
  205. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc_dev_kit.egg-info/top_level.txt +0 -0
  206. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/requirements.txt +0 -0
  207. {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ntc-dev-kit
3
- Version: 1.1.0
3
+ Version: 1.2.1
4
4
  Summary: Nexent Toolkit Console - A CLI tool for scaffolding Django REST Framework, React, Next.js, and Flutter projects
5
5
  Home-page: https://github.com/nexent/ntc
6
6
  Author: Nexent
@@ -97,7 +97,31 @@ ntc init --technology flutter
97
97
  ntc init
98
98
  ```
99
99
 
100
- Navigate menus with **arrow keys**, select with **Enter**, exit with **Escape**.
100
+ A few questions, asked inline: project name, what you're building, where it goes, then the
101
+ options for that technology (sign-in and Python environment for Django; installing dependencies
102
+ for the others), a summary, and **Create?**. The defaults are already selected, so Enter takes
103
+ them; **↑/↓** move, a **number key** picks an option at once, **Esc** cancels without writing
104
+ anything. Each answer collapses to one line, and the only progress shown is for real work
105
+ (creating the environment, installing packages).
106
+
107
+ ### Without questions
108
+
109
+ Every answer can be given as a flag; with `--yes` nothing is asked (scripts, CI, AI agents):
110
+
111
+ ```bash
112
+ ntc init -t django --name shop-api --with-auth --env venv --yes
113
+ ntc init -t nextjs --name site --install --yes
114
+ ```
115
+
116
+ | Flag | Meaning |
117
+ |------|---------|
118
+ | `-t`, `--technology` | `django`, `nextjs`, `react-admin`, `react`, `flutter` |
119
+ | `--name` | Project name (`.` = the current folder) |
120
+ | `--path` | Folder to create the project in (default: current folder) |
121
+ | `--with-auth` / `--no-auth` | Django: include the sign-in app (custom User model, JWT, migration included) |
122
+ | `--env` | Django: `venv`, `conda`, `existing` (the active virtualenv) or `skip` |
123
+ | `--install` / `--no-install` | Others: run `npm install` / `flutter pub get` |
124
+ | `--yes`, `-y` | Ask nothing; needs `--name` and `--technology` |
101
125
 
102
126
  ### Technology Options
103
127
 
@@ -57,7 +57,31 @@ ntc init --technology flutter
57
57
  ntc init
58
58
  ```
59
59
 
60
- Navigate menus with **arrow keys**, select with **Enter**, exit with **Escape**.
60
+ A few questions, asked inline: project name, what you're building, where it goes, then the
61
+ options for that technology (sign-in and Python environment for Django; installing dependencies
62
+ for the others), a summary, and **Create?**. The defaults are already selected, so Enter takes
63
+ them; **↑/↓** move, a **number key** picks an option at once, **Esc** cancels without writing
64
+ anything. Each answer collapses to one line, and the only progress shown is for real work
65
+ (creating the environment, installing packages).
66
+
67
+ ### Without questions
68
+
69
+ Every answer can be given as a flag; with `--yes` nothing is asked (scripts, CI, AI agents):
70
+
71
+ ```bash
72
+ ntc init -t django --name shop-api --with-auth --env venv --yes
73
+ ntc init -t nextjs --name site --install --yes
74
+ ```
75
+
76
+ | Flag | Meaning |
77
+ |------|---------|
78
+ | `-t`, `--technology` | `django`, `nextjs`, `react-admin`, `react`, `flutter` |
79
+ | `--name` | Project name (`.` = the current folder) |
80
+ | `--path` | Folder to create the project in (default: current folder) |
81
+ | `--with-auth` / `--no-auth` | Django: include the sign-in app (custom User model, JWT, migration included) |
82
+ | `--env` | Django: `venv`, `conda`, `existing` (the active virtualenv) or `skip` |
83
+ | `--install` / `--no-install` | Others: run `npm install` / `flutter pub get` |
84
+ | `--yes`, `-y` | Ask nothing; needs `--name` and `--technology` |
61
85
 
62
86
  ### Technology Options
63
87
 
@@ -1,5 +1,5 @@
1
1
  """NTC - Nexent Toolkit Console."""
2
2
 
3
- __version__ = "1.1.0"
3
+ __version__ = "1.2.1"
4
4
  __author__ = "Nexent"
5
5
  __license__ = "MIT"
@@ -0,0 +1,421 @@
1
+ import argparse
2
+ import os
3
+ import sys
4
+ import time
5
+ from colorama import Fore, Style, init
6
+ from ntc.utils.validation import is_valid_project_name
7
+
8
+ # Commands import their generators when they run, so `ntc --help` and the first question of
9
+ # `ntc init` appear without loading every template module first.
10
+
11
+ # Initialize colorama for cross-platform colored output
12
+ init(autoreset=True)
13
+
14
+ def print_banner():
15
+ """Display minimal welcome banner."""
16
+ print(f"\n{Fore.WHITE}{Style.BRIGHT} ntc{Style.RESET_ALL} {Fore.LIGHTBLACK_EX}— project scaffolding tool{Style.RESET_ALL}")
17
+ print()
18
+
19
+ def print_success(message):
20
+ """Print success message with green color and checkmark."""
21
+ print(f"{Fore.GREEN}✅ {message}{Style.RESET_ALL}")
22
+
23
+ def print_error(message):
24
+ """Print error message with red color and X mark."""
25
+ print(f"{Fore.RED}❌ {message}{Style.RESET_ALL}")
26
+
27
+ def print_warning(message):
28
+ """Print warning message with yellow color and warning sign."""
29
+ print(f"{Fore.YELLOW}⚠️ {message}{Style.RESET_ALL}")
30
+
31
+ def print_info(message):
32
+ """Print info message with blue color and info icon."""
33
+ print(f"{Fore.CYAN}ℹ️ {message}{Style.RESET_ALL}")
34
+
35
+ def animate_dependency_install(requirements_path=None, pip_command=None, env_type="system"):
36
+ """Show real dependency installation with progress animation."""
37
+ import subprocess
38
+ import threading
39
+
40
+ if not requirements_path or not os.path.exists(requirements_path):
41
+ # Fallback to simulation if no requirements file
42
+ dependencies = [
43
+ "Resolving dependencies",
44
+ "Downloading packages",
45
+ "Installing Django",
46
+ "Installing DRF",
47
+ "Installing Celery",
48
+ "Installing CORS headers",
49
+ "Configuring environment",
50
+ "Setting up database",
51
+ "Creating migrations",
52
+ "Finalizing setup"
53
+ ]
54
+
55
+ print(f"\n{Fore.MAGENTA}📦 Installing Dependencies{Style.RESET_ALL}")
56
+
57
+ for i, dep in enumerate(dependencies, 1):
58
+ progress = int((i / len(dependencies)) * 100)
59
+
60
+ # Show animation for each dependency
61
+ for j in range(5): # 5 animation cycles per dependency
62
+ frame = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"][j % 10]
63
+ # Clear line and print new content
64
+ print(f"\r{' ' * 80}\r{Fore.CYAN}{frame} {dep}... {Fore.YELLOW}[{progress}%]{Style.RESET_ALL}", end="", flush=True)
65
+ time.sleep(0.2)
66
+
67
+ print(f"\r{' ' * 80}\r{Fore.GREEN}✅ All dependencies installed successfully! {Fore.YELLOW}[100%]{Style.RESET_ALL}")
68
+ print()
69
+ return
70
+
71
+ # Real dependency installation with animation
72
+ print(f"\n{Fore.MAGENTA}📦 Installing Dependencies{Style.RESET_ALL}")
73
+
74
+ # Determine the pip command to use
75
+ if pip_command is None:
76
+ # Default to system pip
77
+ pip_command = [sys.executable, "-m", "pip", "install", "-r", requirements_path]
78
+
79
+ # Animation state
80
+ animation_running = True
81
+ install_complete = False
82
+ install_success = False
83
+ error_message = ""
84
+
85
+ def run_pip_install():
86
+ nonlocal install_complete, install_success, error_message
87
+ try:
88
+ # Run pip install with suppressed output
89
+ result = subprocess.run(
90
+ pip_command,
91
+ capture_output=True,
92
+ text=True,
93
+ check=True
94
+ )
95
+ install_success = True
96
+ except subprocess.CalledProcessError as e:
97
+ install_success = False
98
+ error_message = str(e)
99
+ finally:
100
+ install_complete = True
101
+
102
+ # Start installation in background thread
103
+ install_thread = threading.Thread(target=run_pip_install)
104
+ install_thread.start()
105
+
106
+ # Show animation while installation runs
107
+ frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
108
+ frame_index = 0
109
+
110
+ while not install_complete:
111
+ frame = frames[frame_index % len(frames)]
112
+ # Clear line completely before writing new content
113
+ env_msg = f" ({env_type} environment)" if env_type != "system" else ""
114
+ print(f"\r{' ' * 80}\r{Fore.CYAN}{frame} Installing packages{env_msg}...{Style.RESET_ALL}", end="", flush=True)
115
+ time.sleep(0.1)
116
+ frame_index += 1
117
+
118
+ # Wait for thread to complete
119
+ install_thread.join()
120
+
121
+ # Show final result
122
+ if install_success:
123
+ print(f"\r{' ' * 80}\r{Fore.GREEN}✅ All dependencies installed successfully!{Style.RESET_ALL}")
124
+ else:
125
+ print(f"\r{' ' * 80}\r{Fore.RED}❌ Failed to install dependencies: {error_message}{Style.RESET_ALL}")
126
+ print(f"{Fore.YELLOW}💡 You can manually install later with: pip install -r requirements.txt{Style.RESET_ALL}")
127
+
128
+ print()
129
+
130
+ def run_doctor():
131
+ """Check setup and fix common issues like PATH configuration."""
132
+ import shutil
133
+ import site
134
+
135
+ # Nexent brand palette
136
+ DIM = Fore.LIGHTBLACK_EX
137
+ ACCENT = Fore.WHITE
138
+ OK = Fore.GREEN
139
+ WARN = Fore.YELLOW
140
+ ERR = Fore.RED
141
+ BOLD = Style.BRIGHT
142
+ RESET = Style.RESET_ALL
143
+
144
+ def _ok(text):
145
+ print(f" {OK} {text}{RESET}")
146
+
147
+ def _err(text):
148
+ print(f" {ERR} {text}{RESET}")
149
+
150
+ def _warn(text):
151
+ print(f" {WARN} {text}{RESET}")
152
+
153
+ def _dim(text):
154
+ print(f" {DIM}{text}{RESET}")
155
+
156
+ # Minimal Nexent header
157
+ print(f"\n{BOLD}{Fore.WHITE} NEXENT{RESET}")
158
+ print(f" {DIM}Future Awaits{RESET}")
159
+ print(f" {DIM}{'─' * 50}{RESET}")
160
+ print(f" {Fore.WHITE}SETUP DIAGNOSTICS{RESET}\n")
161
+
162
+ issues_found = 0
163
+ issues_fixed = 0
164
+
165
+ # 1. Python
166
+ print(f" {Fore.WHITE}Python{RESET}")
167
+ py_version = sys.version_info
168
+ if py_version >= (3, 10):
169
+ _ok(f"{py_version.major}.{py_version.minor}.{py_version.micro}")
170
+ else:
171
+ _err(f"{py_version.major}.{py_version.minor} — >= 3.10 required")
172
+ issues_found += 1
173
+
174
+ # 2. Dependencies
175
+ print(f"\n {Fore.WHITE}Dependencies{RESET}")
176
+ deps_ok = True
177
+ for mod, name in [("colorama", "colorama"), ("yaml", "pyyaml")]:
178
+ try:
179
+ __import__(mod)
180
+ _ok(name)
181
+ except ImportError:
182
+ _err(name)
183
+ deps_ok = False
184
+ if not deps_ok:
185
+ issues_found += 1
186
+ _dim(" Run: pip install colorama pyyaml")
187
+
188
+ # 3. Command accessibility
189
+ print(f"\n {Fore.WHITE}Command{RESET}")
190
+ ntc_path = shutil.which("ntc")
191
+ if ntc_path:
192
+ _ok("ntc found")
193
+ _dim(f" {ntc_path}")
194
+ else:
195
+ _err("ntc not in PATH")
196
+ issues_found += 1
197
+
198
+ # 4. Scripts directory
199
+ print(f"\n {Fore.WHITE}Scripts Directory{RESET}")
200
+ scripts_dir = os.path.join(site.USER_BASE, "Scripts")
201
+ if os.path.exists(scripts_dir):
202
+ _ok(scripts_dir)
203
+ else:
204
+ _warn("missing — creating...")
205
+ os.makedirs(scripts_dir, exist_ok=True)
206
+ _ok("created")
207
+ issues_fixed += 1
208
+
209
+ # 5. PATH
210
+ if os.name == "nt":
211
+ print(f"\n {Fore.WHITE}PATH{RESET}")
212
+ current_path = os.environ.get("PATH", "")
213
+ if scripts_dir.lower() in current_path.lower():
214
+ _ok("Scripts directory in PATH")
215
+ else:
216
+ _warn("Scripts directory not in PATH")
217
+ try:
218
+ import winreg
219
+ with winreg.OpenKey(
220
+ winreg.HKEY_CURRENT_USER, "Environment", 0,
221
+ winreg.KEY_READ | winreg.KEY_WRITE,
222
+ ) as key:
223
+ existing, _ = winreg.QueryValueEx(key, "PATH")
224
+ if scripts_dir.lower() not in existing.lower():
225
+ new_path = f"{existing};{scripts_dir}"
226
+ winreg.SetValueEx(key, "PATH", 0, winreg.REG_EXPAND_SZ, new_path)
227
+ os.environ["PATH"] = new_path
228
+ _ok("added to registry")
229
+ issues_fixed += 1
230
+ else:
231
+ _ok("in registry (restart needed)")
232
+ except Exception as e:
233
+ _err(f"registry edit failed: {e}")
234
+ _dim(f" Add manually: {scripts_dir}")
235
+ issues_found += 1
236
+
237
+ # 6. Package version
238
+ print(f"\n {Fore.WHITE}Package{RESET}")
239
+ try:
240
+ import ntc
241
+ _ok(f"ntc-dev-kit {ntc.__version__}")
242
+ except (ImportError, AttributeError):
243
+ _err("ntc-dev-kit not installed")
244
+ issues_found += 1
245
+
246
+ # Summary
247
+ print(f"\n {DIM}{'─' * 50}{RESET}")
248
+ if issues_found == 0 and issues_fixed == 0:
249
+ print(f" {OK}Ready.{RESET}")
250
+ elif issues_fixed > 0:
251
+ print(f" {OK}Fixed {issues_fixed}.{RESET}")
252
+ if issues_found > 0:
253
+ print(f" {WARN}{issues_found} remaining.{RESET}")
254
+ else:
255
+ print(f" {DIM}Restart terminal for changes.{RESET}")
256
+ else:
257
+ print(f" {ERR}{issues_found} issue(s) found.{RESET}")
258
+ print(f" {DIM}Run 'ntc doctor' again after fixing.{RESET}")
259
+ print(f" {DIM}{'─' * 50}{RESET}\n")
260
+
261
+
262
+ def _use_utf8_console():
263
+ """Print emoji and box-drawing characters on any console.
264
+
265
+ A Windows console on cp1252 (the default, and what scripts, CI and editor terminals get)
266
+ can't encode them: the very first print raised UnicodeEncodeError and ntc init crashed.
267
+ Characters the console truly can't show are replaced instead of raising."""
268
+ for stream in (sys.stdout, sys.stderr):
269
+ try:
270
+ stream.reconfigure(encoding='utf-8', errors='replace')
271
+ except (AttributeError, ValueError):
272
+ pass # not a reconfigurable text stream (redirected to an object, Python < 3.7)
273
+
274
+
275
+ def main():
276
+ _use_utf8_console()
277
+ parser = argparse.ArgumentParser(
278
+ description="ntc — project scaffolding tool",
279
+ formatter_class=argparse.RawDescriptionHelpFormatter,
280
+ epilog="""
281
+ Examples:
282
+ ntc init # New project: a few questions, defaults pre-selected
283
+ ntc init -t django # Skip the "what are you building" question
284
+ ntc init -t django --name shop-api --with-auth --env venv --yes
285
+ # No questions at all (scripts, CI, AI agents)
286
+ ntc init -t nextjs --name site --install --yes
287
+ ntc compose myproject # Compose full-stack project with multiple components
288
+ ntc start # Start all services from ntc.yml configuration
289
+ ntc create app myapp # Create new Django app
290
+ ntc drf init path name # Legacy Django REST Framework initialization
291
+ """
292
+ )
293
+
294
+ subparsers = parser.add_subparsers(dest="command", help="Available commands")
295
+
296
+ # Enhanced init command
297
+ init_parser = subparsers.add_parser("init", help="Interactive project initialization")
298
+ init_parser.add_argument('--technology', '-t', type=str,
299
+ choices=['django', 'react', 'react-admin', 'nextjs', 'flutter'],
300
+ help='Technology to use (django, react, react-admin, nextjs, flutter)')
301
+ init_parser.add_argument("--backend",
302
+ choices=["django", "drf", "laravel"],
303
+ help="Backend technology")
304
+ init_parser.add_argument("--frontend",
305
+ choices=["react", "react-admin", "nextjs", "flutter"],
306
+ help="Frontend technology")
307
+ init_parser.add_argument("--name", help="Project name ('.' = the current directory's name)")
308
+ init_parser.add_argument("--path", help="Directory to create the project in (default: current directory)")
309
+ auth_group = init_parser.add_mutually_exclusive_group()
310
+ auth_group.add_argument("--with-auth", dest="with_auth", action="store_true", default=None,
311
+ help="Django: include the authentication app (custom User model, JWT endpoints)")
312
+ auth_group.add_argument("--no-auth", dest="with_auth", action="store_false",
313
+ help="Django: no authentication app")
314
+ init_parser.add_argument("--env", choices=["venv", "conda", "existing", "skip"],
315
+ help="Django: Python environment - create a venv/conda env, install into the "
316
+ "active one, or skip")
317
+ install_group = init_parser.add_mutually_exclusive_group()
318
+ install_group.add_argument("--install", dest="install", action="store_true", default=None,
319
+ help="Next.js/React/Flutter: install dependencies (npm install / flutter pub get)")
320
+ install_group.add_argument("--no-install", dest="install", action="store_false",
321
+ help="Next.js/React/Flutter: don't install dependencies")
322
+ init_parser.add_argument("--yes", "-y", action="store_true",
323
+ help="Don't ask: use the options given and defaults for the rest "
324
+ "(requires --name and --technology)")
325
+
326
+ # Create command for apps
327
+ create_parser = subparsers.add_parser("create", help="Create new components")
328
+ create_subparsers = create_parser.add_subparsers(dest="create_type", help="Type of component to create")
329
+
330
+ # Create app subcommand
331
+ app_parser = create_subparsers.add_parser("app", help="Create new Django app with NTC structure")
332
+ app_parser.add_argument("app_name", help="Name of the app to create")
333
+ url_group = app_parser.add_mutually_exclusive_group()
334
+ url_group.add_argument("--no-url", action="store_true",
335
+ help="Don't add the app's URLs anywhere; include them yourself")
336
+ url_group.add_argument("--url-prefix", default=None,
337
+ help="Add the app's URLs at the root urlpatterns under this prefix "
338
+ "(e.g. 'api/v2/'). Default: the project's api_v1 list (/api/v1/<app>/)")
339
+
340
+ # Legacy DRF command for backward compatibility
341
+ drf_parser = subparsers.add_parser("drf", help="Django REST Framework commands (legacy)")
342
+ drf_subparsers = drf_parser.add_subparsers(dest="drf_action")
343
+ drf_init_parser = drf_subparsers.add_parser("init", help="Initialize DRF project")
344
+ drf_init_parser.add_argument("path", help="Base path to create the project")
345
+ drf_init_parser.add_argument("name", help="Project name")
346
+
347
+ # Composer command for full-stack project composition
348
+ composer_parser = subparsers.add_parser("compose", help="Compose a full-stack project with multiple components")
349
+ composer_parser.add_argument("project_name", help="Name of the project")
350
+ composer_parser.add_argument("--path", help="Base path to create the project (default: current directory)")
351
+
352
+ # Start services command
353
+ start_parser = subparsers.add_parser("start", help="Start all services defined in ntc.yml")
354
+ start_parser.add_argument("--path", help="Path to project root directory (default: current directory)")
355
+
356
+ # Doctor command for setup diagnostics
357
+ doctor_parser = subparsers.add_parser("doctor", help="Check setup and fix common issues")
358
+
359
+ # TUI command for interactive terminal UI
360
+ tui_parser = subparsers.add_parser("tui", help="Launch interactive terminal UI")
361
+
362
+ args = parser.parse_args()
363
+
364
+ if args.command == "init":
365
+ technology = args.technology or args.frontend or args.backend
366
+ technology = {"drf": "django", "laravel": "django"}.get(technology, technology)
367
+ if args.yes and not (args.name and technology):
368
+ print_error("--yes needs --name and --technology, so there is nothing left to ask.")
369
+ sys.exit(2)
370
+ from ntc.wizard import run as run_wizard
371
+ created = run_wizard(technology=technology, name=args.name, path=args.path, with_auth=args.with_auth,
372
+ env_choice=args.env, install=args.install, assume_yes=args.yes)
373
+ sys.exit(0 if created else 1)
374
+ elif args.command == "create" and args.create_type == "app":
375
+ if not is_valid_project_name(args.app_name):
376
+ print_error("App name may only contain letters, numbers, underscores, and hyphens!")
377
+ sys.exit(1)
378
+ from ntc.app_creator import create_app_structure
379
+ create_app_structure(args.app_name, add_urls=not args.no_url, url_prefix=args.url_prefix)
380
+ elif args.command == "drf" and args.drf_action == "init":
381
+ # Legacy support
382
+ if not is_valid_project_name(args.name):
383
+ print_error("Project name may only contain letters, numbers, underscores, and hyphens!")
384
+ sys.exit(1)
385
+ from ntc.templates.drf_structure.main import init_drf_project
386
+ init_drf_project(args.path, args.name)
387
+ elif args.command == "compose":
388
+ # Composer for full-stack project composition
389
+ if not is_valid_project_name(args.project_name):
390
+ print_error("Project name may only contain letters, numbers, underscores, and hyphens!")
391
+ sys.exit(1)
392
+ from ntc.composer.main import compose_project
393
+ if args.path:
394
+ compose_project(args.project_name, args.path)
395
+ else:
396
+ compose_project(args.project_name)
397
+ elif args.command == "start":
398
+ # Start services from ntc.yml configuration
399
+ from ntc.composer.main import start_services
400
+ if args.path:
401
+ start_services(args.path)
402
+ else:
403
+ start_services()
404
+ elif args.command == "doctor":
405
+ run_doctor()
406
+ elif args.command == "tui":
407
+ from ntc.ui.app import run_tui
408
+ run_tui()
409
+ else:
410
+ # Show help if no command provided
411
+ print_banner()
412
+ print(f"\n {Fore.LIGHTBLACK_EX}Quick Start:{Style.RESET_ALL}")
413
+ print(f" {Fore.WHITE}ntc tui{Style.RESET_ALL} {Fore.LIGHTBLACK_EX}interactive terminal UI{Style.RESET_ALL}")
414
+ print(f" {Fore.WHITE}ntc init{Style.RESET_ALL} {Fore.LIGHTBLACK_EX}interactive setup{Style.RESET_ALL}")
415
+ print(f" {Fore.WHITE}ntc init -t django{Style.RESET_ALL} {Fore.LIGHTBLACK_EX}Django project{Style.RESET_ALL}")
416
+ print(f" {Fore.WHITE}ntc doctor{Style.RESET_ALL} {Fore.LIGHTBLACK_EX}check setup{Style.RESET_ALL}")
417
+ print()
418
+ parser.print_help()
419
+
420
+ if __name__ == "__main__":
421
+ main()
@@ -37,8 +37,8 @@ modules_to_register = [
37
37
  ("ntc.templates.flutter", os.path.join(this_dir, "templates", "flutter", "__init__.py")),
38
38
  ("ntc.templates.flutter.main", os.path.join(this_dir, "templates", "flutter", "main.py")),
39
39
  ("ntc.templates.auth_app", os.path.join(this_dir, "templates", "auth_app", "__init__.py")),
40
- ("ntc.ui", os.path.join(this_dir, "ui", "__init__.py")),
41
- ("ntc.ui.app", os.path.join(this_dir, "ui", "app.py")),
40
+ # Not ntc.ui / ntc.ui.app: they import textual (most of a second), and `ntc tui` imports
41
+ # them itself when it runs.
42
42
  ]
43
43
 
44
44
  for modname, file_path in modules_to_register:
@@ -0,0 +1,51 @@
1
+ # Generated by Django 5.2.7 on 2026-10-20 16:26
2
+
3
+ import apps.authentication.models
4
+ import django.contrib.auth.validators
5
+ import django.db.models.functions.text
6
+ import django.utils.timezone
7
+ from django.db import migrations, models
8
+
9
+
10
+ class Migration(migrations.Migration):
11
+
12
+ initial = True
13
+
14
+ dependencies = [
15
+ ('auth', '0012_alter_user_first_name_max_length'),
16
+ ]
17
+
18
+ operations = [
19
+ migrations.CreateModel(
20
+ name='User',
21
+ fields=[
22
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
23
+ ('password', models.CharField(max_length=128, verbose_name='password')),
24
+ ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
25
+ ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
26
+ ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
27
+ ('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')),
28
+ ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
29
+ ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
30
+ ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
31
+ ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
32
+ ('email', models.EmailField(max_length=254, unique=True)),
33
+ ('phone_number', models.CharField(blank=True, max_length=15)),
34
+ ('date_of_birth', models.DateField(blank=True, null=True)),
35
+ ('profile_picture', models.ImageField(blank=True, null=True, upload_to='profile_pics/')),
36
+ ('is_verified', models.BooleanField(default=False)),
37
+ ('created_at', models.DateTimeField(auto_now_add=True)),
38
+ ('updated_at', models.DateTimeField(auto_now=True)),
39
+ ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')),
40
+ ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')),
41
+ ],
42
+ options={
43
+ 'verbose_name': 'User',
44
+ 'verbose_name_plural': 'Users',
45
+ 'constraints': [models.UniqueConstraint(django.db.models.functions.text.Lower('email'), name='authentication_user_email_ci_unique')],
46
+ },
47
+ managers=[
48
+ ('objects', apps.authentication.models.UserManager()),
49
+ ],
50
+ ),
51
+ ]
@@ -0,0 +1,54 @@
1
+ from django.contrib.auth.models import AbstractUser, UserManager as DjangoUserManager
2
+ from django.db import models
3
+ from django.db.models.functions import Lower
4
+
5
+
6
+ def normalize_email(email):
7
+ """Emails are stored lowercase: A@x.com and a@x.com are the same person, and sign-in and
8
+ password resets look them up exactly."""
9
+ return (email or '').strip().lower()
10
+
11
+
12
+ class UserManager(DjangoUserManager):
13
+ def _create_user(self, username, email, password, **extra_fields):
14
+ return super()._create_user(username, normalize_email(email), password, **extra_fields)
15
+
16
+ def get_by_natural_key(self, username):
17
+ # USERNAME_FIELD is the email: sign-in matches it whatever the case typed.
18
+ return self.get(email=normalize_email(username))
19
+
20
+
21
+ class User(AbstractUser):
22
+ """Custom User model extending AbstractUser. first_name/last_name keep AbstractUser's 150 characters."""
23
+ email = models.EmailField(unique=True)
24
+ phone_number = models.CharField(max_length=15, blank=True)
25
+ date_of_birth = models.DateField(null=True, blank=True)
26
+ # Stored in R2 when it's configured (STORAGES in settings), on disk in development.
27
+ profile_picture = models.ImageField(upload_to='profile_pics/', null=True, blank=True)
28
+ is_verified = models.BooleanField(default=False)
29
+ created_at = models.DateTimeField(auto_now_add=True)
30
+ updated_at = models.DateTimeField(auto_now=True)
31
+
32
+ objects = UserManager()
33
+
34
+ USERNAME_FIELD = 'email'
35
+ REQUIRED_FIELDS = ['username']
36
+
37
+ class Meta:
38
+ verbose_name = 'User'
39
+ verbose_name_plural = 'Users'
40
+ constraints = [
41
+ # Belt and braces with the lowercase normalization: no two accounts differ only by case.
42
+ models.UniqueConstraint(Lower('email'), name='authentication_user_email_ci_unique'),
43
+ ]
44
+
45
+ def save(self, *args, **kwargs):
46
+ self.email = normalize_email(self.email)
47
+ super().save(*args, **kwargs)
48
+
49
+ def __str__(self):
50
+ return self.email
51
+
52
+ @property
53
+ def full_name(self) -> str:
54
+ return f"{self.first_name} {self.last_name}".strip()
@@ -1,7 +1,7 @@
1
1
  from rest_framework import serializers
2
2
  from django.contrib.auth import authenticate
3
3
  from django.contrib.auth.password_validation import validate_password
4
- from .models import User
4
+ from .models import User, normalize_email
5
5
 
6
6
 
7
7
  class UserRegistrationSerializer(serializers.ModelSerializer):
@@ -13,6 +13,13 @@ class UserRegistrationSerializer(serializers.ModelSerializer):
13
13
  model = User
14
14
  fields = ('email', 'username', 'password', 'password_confirm', 'first_name', 'last_name')
15
15
 
16
+ def validate_email(self, value):
17
+ # Case-insensitive, like the database constraint: A@x.com is already taken by a@x.com.
18
+ value = normalize_email(value)
19
+ if User.objects.filter(email=value).exists():
20
+ raise serializers.ValidationError('An account with this email already exists.')
21
+ return value
22
+
16
23
  def validate(self, attrs):
17
24
  if attrs['password'] != attrs['password_confirm']:
18
25
  raise serializers.ValidationError({'password_confirm': "Passwords don't match."})