ntc-dev-kit 1.3.1__tar.gz → 1.4.0__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 (233) hide show
  1. {ntc_dev_kit-1.3.1/ntc_dev_kit.egg-info → ntc_dev_kit-1.4.0}/PKG-INFO +32 -2
  2. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/README.md +31 -1
  3. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/__init__.py +1 -1
  4. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/cli_enhanced.py +7 -0
  5. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/next.config.js +5 -0
  6. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/vite.config.ts +2 -2
  7. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_structure/App.css +12 -0
  8. ntc_dev_kit-1.4.0/ntc/templates/react_structure/src/App.tsx +79 -0
  9. ntc_dev_kit-1.4.0/ntc/templates/react_structure/src/utils/constants.ts +1 -0
  10. ntc_dev_kit-1.4.0/ntc/templates/react_structure/vite.config.ts +13 -0
  11. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/wizard.py +6 -0
  12. ntc_dev_kit-1.4.0/ntc/workspace/adopt.py +316 -0
  13. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/workspace/config.py +51 -9
  14. ntc_dev_kit-1.4.0/ntc/workspace/console_window.py +48 -0
  15. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/workspace/create.py +3 -4
  16. ntc_dev_kit-1.4.0/ntc/workspace/origins.py +182 -0
  17. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/workspace/processes.py +4 -0
  18. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/workspace/screen.py +17 -2
  19. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/workspace/setup.py +24 -11
  20. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/workspace/start.py +7 -1
  21. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0/ntc_dev_kit.egg-info}/PKG-INFO +32 -2
  22. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc_dev_kit.egg-info/SOURCES.txt +4 -0
  23. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/pyproject.toml +1 -1
  24. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/setup.py +1 -1
  25. ntc_dev_kit-1.4.0/tests/test_adopt.py +108 -0
  26. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/tests/test_workspace.py +36 -5
  27. ntc_dev_kit-1.3.1/ntc/templates/react_structure/src/App.tsx +0 -63
  28. ntc_dev_kit-1.3.1/ntc/templates/react_structure/src/utils/constants.ts +0 -1
  29. ntc_dev_kit-1.3.1/ntc/templates/react_structure/vite.config.ts +0 -12
  30. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/LICENSE +0 -0
  31. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/MANIFEST.in +0 -0
  32. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/app_creator.py +0 -0
  33. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/cli.py +0 -0
  34. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/composer/__init__.py +0 -0
  35. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/composer/main.py +0 -0
  36. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/deployment/__init__.py +0 -0
  37. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/main.py +0 -0
  38. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/__init__.py +0 -0
  39. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/auth_app/__init__.py +0 -0
  40. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/auth_app/admin.py +0 -0
  41. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/auth_app/apps.py +0 -0
  42. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/auth_app/migrations/0001_initial.py +0 -0
  43. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/auth_app/models.py +0 -0
  44. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/auth_app/serializers.py +0 -0
  45. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/auth_app/tasks.py +0 -0
  46. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/auth_app/templates/authentication/password_reset.html +0 -0
  47. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/auth_app/templates/authentication/password_reset.txt +0 -0
  48. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/auth_app/tests.py +0 -0
  49. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/auth_app/urls.py +0 -0
  50. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/auth_app/views.py +0 -0
  51. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/__init__.py +0 -0
  52. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/drf_structure.md +0 -0
  53. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/main.py +0 -0
  54. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/structure.json +0 -0
  55. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/.entry/start_core_backend.sh +0 -0
  56. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/.entry/start_tasks_backend.sh +0 -0
  57. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/CLAUDE.md +0 -0
  58. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/README.md +0 -0
  59. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/apps/app_admin.py +0 -0
  60. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/apps/app_apps.py +0 -0
  61. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/apps/app_models.py +0 -0
  62. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/apps/app_urls.py +0 -0
  63. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/apps/app_views.py +0 -0
  64. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/apps/common_apps.py +0 -0
  65. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/apps/common_checks.py +0 -0
  66. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/apps/common_models.py +0 -0
  67. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/ci/github_ci.yml +0 -0
  68. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/ci/pre_commit.yaml +0 -0
  69. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/config/gunicorn_config.py +0 -0
  70. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/config/http.conf +0 -0
  71. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/core/exceptions/handler.py +0 -0
  72. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/core/formatter.py +0 -0
  73. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/core/health.py +0 -0
  74. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/core/logger.py +0 -0
  75. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/core/pagination.py +0 -0
  76. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/core/renderers.py +0 -0
  77. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/core/response_wrapper.py +0 -0
  78. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/core/schema.py +0 -0
  79. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/core/services/azure/email/backend.py +0 -0
  80. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/core/services/azure/email/client.py +0 -0
  81. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/core/services/azure/email/notifier.py +0 -0
  82. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/core/services/object_storage.py +0 -0
  83. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/core/tests.py +0 -0
  84. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/core/utils/request.py +0 -0
  85. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/django_project/asgi.py +0 -0
  86. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/django_project/celery.py +0 -0
  87. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/django_project/dev_settings.py +0 -0
  88. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/django_project/django_settings.txt +0 -0
  89. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/django_project/prod_settings.py +0 -0
  90. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/django_project/project_init.py +0 -0
  91. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/django_project/schema.py +0 -0
  92. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/django_project/test_settings.py +0 -0
  93. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/django_project/urls.py +0 -0
  94. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/django_project/wsgi.py +0 -0
  95. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/docker/Dockerfile +0 -0
  96. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/docker/captain-definition +0 -0
  97. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/docker/docker-compose.yml +0 -0
  98. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/knowledge/knowledge_description.md +0 -0
  99. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/knowledge/knowledge_design_pattern.md +0 -0
  100. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/knowledge/knowledge_structure.md +0 -0
  101. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/root/README.md +0 -0
  102. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/root/django_env.txt +0 -0
  103. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/root/django_requirements.txt +0 -0
  104. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/root/django_requirements_dev.txt +0 -0
  105. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/root/dockerignore.txt +0 -0
  106. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/root/gitattributes.txt +0 -0
  107. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/root/gitignore.txt +0 -0
  108. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/root/manage.py +0 -0
  109. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/root/pytest_ini.txt +0 -0
  110. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/drf_structure/templates/root/ruff_toml.txt +0 -0
  111. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/flutter/.gitignore +0 -0
  112. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/flutter/.knowledge/knowledge_description.md +0 -0
  113. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/flutter/.knowledge/knowledge_design_pattern.md +0 -0
  114. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/flutter/.knowledge/knowledge_structure.md +0 -0
  115. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/flutter/CLAUDE.md +0 -0
  116. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/flutter/__init__.py +0 -0
  117. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/flutter/analysis_options.yaml +0 -0
  118. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/flutter/lib/core/config/app_config.dart +0 -0
  119. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/flutter/lib/core/config/environment.dart +0 -0
  120. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/flutter/lib/core/constants/app_constants.dart +0 -0
  121. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/flutter/lib/core/network/dio_client.dart +0 -0
  122. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/flutter/lib/features/home/presentation/home_screen.dart +0 -0
  123. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/flutter/lib/l10n/app_localizations.dart +0 -0
  124. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/flutter/lib/main.dart +0 -0
  125. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/flutter/lib/services/api_service.dart +0 -0
  126. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/flutter/lib/shared/localization/locale_provider.dart +0 -0
  127. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/flutter/lib/shared/theme/app_theme.dart +0 -0
  128. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/flutter/lib/shared/theme/theme_provider.dart +0 -0
  129. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/flutter/main.py +0 -0
  130. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/flutter/pubspec.yaml +0 -0
  131. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/flutter/structure.json +0 -0
  132. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/.env.example +0 -0
  133. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/.gitignore +0 -0
  134. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/CLAUDE.md +0 -0
  135. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/README.md +0 -0
  136. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/__init__.py +0 -0
  137. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/eslint.config.mjs +0 -0
  138. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/knowledge/knowledge_description.md +0 -0
  139. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/knowledge/knowledge_design_pattern.md +0 -0
  140. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/knowledge/knowledge_structure.md +0 -0
  141. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/main.py +0 -0
  142. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/package.json +0 -0
  143. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/public/logo.svg +0 -0
  144. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/src/app/about/page.tsx +0 -0
  145. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/src/app/contact/page.tsx +0 -0
  146. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/src/app/error.tsx +0 -0
  147. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/src/app/globals.css +0 -0
  148. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/src/app/icon.svg +0 -0
  149. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/src/app/layout.tsx +0 -0
  150. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/src/app/loading.tsx +0 -0
  151. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/src/app/manifest.ts +0 -0
  152. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/src/app/not-found.tsx +0 -0
  153. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/src/app/opengraph-image.tsx +0 -0
  154. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/src/app/page.tsx +0 -0
  155. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/src/app/robots.ts +0 -0
  156. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/src/app/sitemap.ts +0 -0
  157. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/src/components/Footer.tsx +0 -0
  158. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/src/components/Header.tsx +0 -0
  159. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/src/services/api.ts +0 -0
  160. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/src/services/server.ts +0 -0
  161. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/src/utils/constants.ts +0 -0
  162. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/structure.json +0 -0
  163. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/nextjs/tsconfig.json +0 -0
  164. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/.env.example +0 -0
  165. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/.gitignore +0 -0
  166. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/CLAUDE.md +0 -0
  167. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/README.md +0 -0
  168. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/__init__.py +0 -0
  169. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/eslint.config.js +0 -0
  170. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/index.html +0 -0
  171. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/knowledge/knowledge_description.md +0 -0
  172. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/knowledge/knowledge_design_pattern.md +0 -0
  173. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/knowledge/knowledge_structure.md +0 -0
  174. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/main.py +0 -0
  175. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/package.json +0 -0
  176. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/public/favicon.svg +0 -0
  177. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/src/App.tsx +0 -0
  178. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/src/components/ProtectedRoute.tsx +0 -0
  179. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/src/components/Sidebar.tsx +0 -0
  180. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/src/components/TopNav.tsx +0 -0
  181. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/src/context/AuthContext.tsx +0 -0
  182. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/src/context/ThemeContext.tsx +0 -0
  183. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/src/context/WorkspaceContext.tsx +0 -0
  184. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/src/hooks/useApi.ts +0 -0
  185. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/src/index.css +0 -0
  186. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/src/layouts/AuthLayout.tsx +0 -0
  187. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/src/layouts/DashboardLayout.tsx +0 -0
  188. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/src/main.tsx +0 -0
  189. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/src/pages/DashboardPage.tsx +0 -0
  190. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/src/pages/auth/LoginPage.tsx +0 -0
  191. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/src/services/api.service.ts +0 -0
  192. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/src/services/base.service.ts +0 -0
  193. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/src/services/token.ts +0 -0
  194. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/src/vite-env.d.ts +0 -0
  195. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/structure.json +0 -0
  196. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/tsconfig.app.json +0 -0
  197. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/tsconfig.json +0 -0
  198. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_admin/tsconfig.node.json +0 -0
  199. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_structure/CLAUDE.md +0 -0
  200. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_structure/__init__.py +0 -0
  201. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_structure/index.css +0 -0
  202. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_structure/index.html +0 -0
  203. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_structure/knowledge/knowledge_description.md +0 -0
  204. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_structure/knowledge/knowledge_design_pattern.md +0 -0
  205. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_structure/knowledge/knowledge_structure.md +0 -0
  206. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_structure/main.py +0 -0
  207. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_structure/package.json +0 -0
  208. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_structure/public/favicon.svg +0 -0
  209. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_structure/src/components/Header.tsx +0 -0
  210. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_structure/src/main.tsx +0 -0
  211. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_structure/src/services/api.ts +0 -0
  212. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_structure/src/vite-env.d.ts +0 -0
  213. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_structure/structure.json +0 -0
  214. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_structure/tsconfig.app.json +0 -0
  215. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_structure/tsconfig.json +0 -0
  216. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/templates/react_structure/tsconfig.node.json +0 -0
  217. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/ui/__init__.py +0 -0
  218. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/ui/app.py +0 -0
  219. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/utils/__init__.py +0 -0
  220. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/utils/environment.py +0 -0
  221. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/utils/output.py +0 -0
  222. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/utils/prompts.py +0 -0
  223. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/utils/shell.py +0 -0
  224. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/utils/validation.py +0 -0
  225. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/workspace/__init__.py +0 -0
  226. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc/workspace/clean.py +0 -0
  227. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc_dev_kit.egg-info/dependency_links.txt +0 -0
  228. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc_dev_kit.egg-info/entry_points.txt +0 -0
  229. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc_dev_kit.egg-info/requires.txt +0 -0
  230. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/ntc_dev_kit.egg-info/top_level.txt +0 -0
  231. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/requirements.txt +0 -0
  232. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/setup.cfg +0 -0
  233. {ntc_dev_kit-1.3.1 → ntc_dev_kit-1.4.0}/tests/test_templates.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ntc-dev-kit
3
- Version: 1.3.1
3
+ Version: 1.4.0
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
@@ -163,11 +163,41 @@ In `ntc start`, **All** is a grid of up to four terminals, one per app (**[** an
163
163
  through more), with each app's port and status on its border and its output colour-coded: errors
164
164
  red, warnings amber, ready/compiled green, HTTP status codes by class. **Tab** moves between the
165
165
  terminals and the keys act on the focused one: **r** restarts, **s** stops or starts, **o** opens
166
- it in the browser, **i** types into it (Flutter's `r` hot reload), **c** clears, **Enter** opens it
166
+ it in the browser (**Ctrl+O** opens every app that's running), **i** types into it (Flutter's `r` hot reload), **c** clears, **Enter** opens it
167
167
  full screen (**Esc** comes back). **1-9** jump to an app's own tab, **0** back to all, **q** quits
168
168
  and stops everything. Apps wait for the ones they `need` (the backend) before starting, and a port
169
169
  that's already taken is reported instead of crashing.
170
170
 
171
+ #### The backend allows the frontends
172
+
173
+ Whenever `ntc start` runs a Django app, it adds every frontend's origin (`http://127.0.0.1:<port>`
174
+ and `http://localhost:<port>`) to the backend's `CORS_ALLOWED_ORIGINS` and `CSRF_TRUSTED_ORIGINS`,
175
+ so the browser apps can call the API whatever ports they're on. It reads which environment
176
+ variables the settings use and keeps what they already allow (from the environment, the app's
177
+ `.env`, or the settings' default); only the backend's process gets the result, nothing is edited.
178
+ A setting written out in the settings (not read from the environment) can't be extended from
179
+ outside: the backend's terminal then says which origins to add. `allow_origins = false` in the
180
+ app's section turns this off. New workspaces also get the origins in the backend's `.env`, for
181
+ running it on its own.
182
+
183
+ #### Existing projects: `ntc adopt`
184
+
185
+ Apps that weren't made by ntc can run with it too. In the folder that holds them:
186
+
187
+ ```bash
188
+ ntc adopt # finds the apps, shows what it found, writes ntc.toml
189
+ ntc adopt --dry-run # just show
190
+ ```
191
+
192
+ It recognises Django (`manage.py`), Next.js, Vite and other Node apps (`package.json` with a
193
+ `dev`/`start` script) and Flutter, and keeps how each already runs: its package manager (npm,
194
+ pnpm, yarn, bun), its `dev` script and port (a port clash is moved with `--port`), and its Python
195
+ environment (`env/`, `venv/`, `.venv/`, or a conda env named after the project). Folders that
196
+ aren't apps (documents, tooling) are left out, and a shared name prefix is dropped
197
+ (`shop-backend` → `backend`). Only `ntc.toml` is written: nothing in the apps changes, no env
198
+ values are overridden, and setup never runs `migrate` (the project's `.env` may point at a shared
199
+ database). `ntc setup` copies `.env.example` when an app has no env file yet.
200
+
171
201
  `ntc clean` only removes what the tools rebuild (`.next`, `dist`, `__pycache__`, caches; with
172
202
  `--deep` also `node_modules` and `env`), never anything git tracks, and skips apps that are running.
173
203
  `--dry-run` shows what would go.
@@ -122,11 +122,41 @@ In `ntc start`, **All** is a grid of up to four terminals, one per app (**[** an
122
122
  through more), with each app's port and status on its border and its output colour-coded: errors
123
123
  red, warnings amber, ready/compiled green, HTTP status codes by class. **Tab** moves between the
124
124
  terminals and the keys act on the focused one: **r** restarts, **s** stops or starts, **o** opens
125
- it in the browser, **i** types into it (Flutter's `r` hot reload), **c** clears, **Enter** opens it
125
+ it in the browser (**Ctrl+O** opens every app that's running), **i** types into it (Flutter's `r` hot reload), **c** clears, **Enter** opens it
126
126
  full screen (**Esc** comes back). **1-9** jump to an app's own tab, **0** back to all, **q** quits
127
127
  and stops everything. Apps wait for the ones they `need` (the backend) before starting, and a port
128
128
  that's already taken is reported instead of crashing.
129
129
 
130
+ #### The backend allows the frontends
131
+
132
+ Whenever `ntc start` runs a Django app, it adds every frontend's origin (`http://127.0.0.1:<port>`
133
+ and `http://localhost:<port>`) to the backend's `CORS_ALLOWED_ORIGINS` and `CSRF_TRUSTED_ORIGINS`,
134
+ so the browser apps can call the API whatever ports they're on. It reads which environment
135
+ variables the settings use and keeps what they already allow (from the environment, the app's
136
+ `.env`, or the settings' default); only the backend's process gets the result, nothing is edited.
137
+ A setting written out in the settings (not read from the environment) can't be extended from
138
+ outside: the backend's terminal then says which origins to add. `allow_origins = false` in the
139
+ app's section turns this off. New workspaces also get the origins in the backend's `.env`, for
140
+ running it on its own.
141
+
142
+ #### Existing projects: `ntc adopt`
143
+
144
+ Apps that weren't made by ntc can run with it too. In the folder that holds them:
145
+
146
+ ```bash
147
+ ntc adopt # finds the apps, shows what it found, writes ntc.toml
148
+ ntc adopt --dry-run # just show
149
+ ```
150
+
151
+ It recognises Django (`manage.py`), Next.js, Vite and other Node apps (`package.json` with a
152
+ `dev`/`start` script) and Flutter, and keeps how each already runs: its package manager (npm,
153
+ pnpm, yarn, bun), its `dev` script and port (a port clash is moved with `--port`), and its Python
154
+ environment (`env/`, `venv/`, `.venv/`, or a conda env named after the project). Folders that
155
+ aren't apps (documents, tooling) are left out, and a shared name prefix is dropped
156
+ (`shop-backend` → `backend`). Only `ntc.toml` is written: nothing in the apps changes, no env
157
+ values are overridden, and setup never runs `migrate` (the project's `.env` may point at a shared
158
+ database). `ntc setup` copies `.env.example` when an app has no env file yet.
159
+
130
160
  `ntc clean` only removes what the tools rebuild (`.next`, `dist`, `__pycache__`, caches; with
131
161
  `--deep` also `node_modules` and `env`), never anything git tracks, and skips apps that are running.
132
162
  `--dry-run` shows what would go.
@@ -1,5 +1,5 @@
1
1
  """NTC - Nexent Toolkit Console."""
2
2
 
3
- __version__ = "1.3.1"
3
+ __version__ = "1.4.0"
4
4
  __author__ = "Nexent"
5
5
  __license__ = "MIT"
@@ -363,6 +363,10 @@ Examples:
363
363
  add_install.add_argument("--install", dest="install", action="store_true", default=None)
364
364
  add_install.add_argument("--no-install", dest="install", action="store_false")
365
365
  add_parser.add_argument("--yes", "-y", action="store_true", help="Don't ask")
366
+ adopt_parser = subparsers.add_parser("adopt", help="Run existing apps (not made by ntc) with ntc: writes ntc.toml")
367
+ adopt_parser.add_argument("path", nargs="?", help="The folder holding the apps (default: current folder)")
368
+ adopt_parser.add_argument("--dry-run", action="store_true", help="Show what was found, write nothing")
369
+ adopt_parser.add_argument("--yes", "-y", action="store_true", help="Don't ask: take the best guesses")
366
370
  setup_parser = subparsers.add_parser("setup", help="Install the workspace's apps (after a clone or a deep clean)")
367
371
  setup_parser.add_argument("apps", nargs="*", help="Only these apps")
368
372
  clean_parser = subparsers.add_parser("clean", help="Delete build output and caches; say how much space it freed")
@@ -424,6 +428,9 @@ Examples:
424
428
  elif args.command == "start":
425
429
  from ntc.workspace.start import run as run_start
426
430
  sys.exit(0 if run_start(args.apps, plain=args.plain) else 1)
431
+ elif args.command == "adopt":
432
+ from ntc.workspace.adopt import run as run_adopt
433
+ sys.exit(0 if run_adopt(args.path, assume_yes=args.yes, dry_run=args.dry_run) else 1)
427
434
  elif args.command == "setup":
428
435
  from ntc.workspace.setup import run as run_setup
429
436
  sys.exit(0 if run_setup(args.apps) else 1)
@@ -1,6 +1,11 @@
1
1
  /** @type {import('next').NextConfig} */
2
2
  const nextConfig = {
3
3
  reactStrictMode: true,
4
+ // The dev server serves on localhost; opened as 127.0.0.1 (as ntc start may), its hot reload
5
+ // would be blocked as cross-origin.
6
+ allowedDevOrigins: ['127.0.0.1', 'localhost'],
7
+ // The project folder is the root, even when a package-lock.json sits in a folder above it.
8
+ turbopack: { root: __dirname },
4
9
  images: {
5
10
  // Hosts next/image may load from, e.g.
6
11
  // { protocol: 'https', hostname: 'media.example.com', pathname: '/**' }
@@ -1,13 +1,13 @@
1
1
  import { defineConfig } from 'vite'
2
2
  import react from '@vitejs/plugin-react'
3
3
  import tailwindcss from '@tailwindcss/vite'
4
- import path from 'path'
4
+ import { fileURLToPath } from 'node:url'
5
5
 
6
6
  export default defineConfig({
7
7
  plugins: [react(), tailwindcss()],
8
8
  resolve: {
9
9
  alias: {
10
- '@': path.resolve(__dirname, './src'),
10
+ '@': fileURLToPath(new URL('./src', import.meta.url)),
11
11
  },
12
12
  },
13
13
  })
@@ -42,6 +42,18 @@
42
42
  background: rgba(244, 67, 54, 0.3);
43
43
  }
44
44
 
45
+ .status-indicator.degraded {
46
+ background: rgba(255, 152, 0, 0.35);
47
+ }
48
+
49
+ .api-problems,
50
+ .api-hint {
51
+ margin-top: 0.75rem;
52
+ list-style: none;
53
+ font-size: 0.9rem;
54
+ opacity: 0.9;
55
+ }
56
+
45
57
  .status-indicator.checking {
46
58
  background: rgba(255, 193, 7, 0.3);
47
59
  }
@@ -0,0 +1,79 @@
1
+ import { useState, useEffect } from 'react'
2
+ import './App.css'
3
+ import Header from './components/Header'
4
+ import axios from 'axios'
5
+ import { apiService } from './services/api'
6
+ import { API_BASE_URL } from './utils/constants'
7
+
8
+ type ApiStatus = 'checking' | 'connected' | 'degraded' | 'disconnected'
9
+
10
+ function App() {
11
+ const [apiStatus, setApiStatus] = useState<ApiStatus>('checking')
12
+ const [problems, setProblems] = useState<string[]>([])
13
+
14
+ useEffect(() => {
15
+ // The ntc Django template's /health/ answers 503 when the API is up but a service it uses
16
+ // (the database, Redis) isn't: that is "connected, degraded", not "disconnected".
17
+ const checkApiConnection = async () => {
18
+ try {
19
+ await apiService.get('/health/')
20
+ setApiStatus('connected')
21
+ } catch (error) {
22
+ const response = axios.isAxiosError(error) ? error.response : undefined
23
+ if (!response) {
24
+ setApiStatus('disconnected') // nothing answered: not running, wrong URL, or CORS
25
+ return
26
+ }
27
+ const services = (response.data?.errors ?? {}) as Record<string, { ok?: boolean; problem?: string }>
28
+ setProblems(Object.entries(services)
29
+ .filter(([, service]) => service && typeof service === 'object' && service.ok === false)
30
+ .map(([name, service]) => `${name}: ${service.problem}`))
31
+ setApiStatus('degraded')
32
+ }
33
+ }
34
+
35
+ checkApiConnection()
36
+ }, [])
37
+
38
+ return (
39
+ <div className="App">
40
+ <Header />
41
+ <main className="App-main">
42
+ <div className="welcome-section">
43
+ <h1>Welcome to {import.meta.env.VITE_APP_NAME || '{project_name}'}</h1>
44
+ <p>Your API-driven React application is ready!</p>
45
+
46
+ <div className="api-status">
47
+ <h3>API Connection Status:</h3>
48
+ <div className={`status-indicator ${apiStatus}`}>
49
+ {apiStatus === 'checking' && '🔄 Checking...'}
50
+ {apiStatus === 'connected' && '✅ Connected'}
51
+ {apiStatus === 'degraded' && '⚠️ Connected, but a service is down'}
52
+ {apiStatus === 'disconnected' && '❌ Disconnected'}
53
+ </div>
54
+ {problems.length > 0 && (
55
+ <ul className="api-problems">
56
+ {problems.map((problem) => <li key={problem}>{problem}</li>)}
57
+ </ul>
58
+ )}
59
+ {apiStatus === 'disconnected' && (
60
+ <p className="api-hint">Is the backend running? It should be at {API_BASE_URL}.</p>
61
+ )}
62
+ </div>
63
+
64
+ <div className="getting-started">
65
+ <h3>Getting Started:</h3>
66
+ <ol>
67
+ <li>Start your backend API server</li>
68
+ <li>Update API_BASE_URL in .env file if needed</li>
69
+ <li>Begin building your components in src/components/</li>
70
+ <li>Add API calls using the apiService in src/services/</li>
71
+ </ol>
72
+ </div>
73
+ </div>
74
+ </main>
75
+ </div>
76
+ )
77
+ }
78
+
79
+ export default App
@@ -0,0 +1 @@
1
+ export const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://127.0.0.1:8000/api/v1' // the ntc Django template's API
@@ -0,0 +1,13 @@
1
+ import { defineConfig } from 'vite'
2
+ import react from '@vitejs/plugin-react'
3
+ import { fileURLToPath } from 'node:url'
4
+
5
+ export default defineConfig({
6
+ plugins: [react()],
7
+ resolve: {
8
+ alias: {
9
+ // Not __dirname: Vite's native config loader (the coming default) runs it as ESM.
10
+ '@': fileURLToPath(new URL('./src', import.meta.url)),
11
+ },
12
+ },
13
+ })
@@ -255,6 +255,12 @@ def run(technology=None, name=None, path=None, with_auth=None, env_choice=None,
255
255
  inside = workspace_config.find_root()
256
256
  if inside and not apps and technology is None:
257
257
  prompts.info(f"You're in the {os.path.basename(inside)} workspace - `ntc add` adds an app to it.")
258
+ elif not inside and not apps and technology is None:
259
+ from ntc.workspace.adopt import detect
260
+ existing, _ = detect(os.getcwd())
261
+ if existing:
262
+ prompts.info(f"This folder already has {len(existing)} app{'s' if len(existing) != 1 else ''} "
263
+ f"({', '.join(a.folder for a in existing)}) - `ntc adopt` sets them up for ntc start.")
258
264
  plan = Plan()
259
265
  try:
260
266
  if name is None:
@@ -0,0 +1,316 @@
1
+ """`ntc adopt`: run existing apps (not made by ntc) with ntc start / setup / clean.
2
+
3
+ It looks at the folder's subfolders (or the folder itself, when that is an app), works out what
4
+ each one is and how it already runs - its package manager, its dev script and port, its Python
5
+ environment - and writes ntc.toml. Nothing inside the apps is changed: no files written, no env
6
+ overrides, and no `migrate` in setup, since an existing project's .env may point at a shared or
7
+ production database.
8
+ """
9
+ import glob
10
+ import json
11
+ import os
12
+ import re
13
+ import time
14
+
15
+ from colorama import Fore, Style
16
+
17
+ from ntc.utils import prompts
18
+ from ntc.workspace import config
19
+
20
+ DIM = Fore.LIGHTBLACK_EX
21
+ RESET = Style.RESET_ALL
22
+
23
+ DEFAULT_PORTS = {"django": 8000, "nextjs": 3000, "vite": 5173, "node": 3000}
24
+
25
+
26
+ class Detected:
27
+ def __init__(self, folder, kind):
28
+ self.folder = folder # path relative to the workspace root ("." for the root)
29
+ self.kind = kind
30
+ self.name = folder
31
+ self.port = None
32
+ self.port_fixed = False # the dev script sets it: can't move it without editing the app
33
+ self.manager = "npm"
34
+ self.script = "dev"
35
+ self.python = None # (path, kind, description) for Django
36
+ self.python_options = []
37
+ self.notes = []
38
+
39
+
40
+ # ── Detection ───────────────────────────────────────────────────────────
41
+
42
+ def _read_json(path):
43
+ try:
44
+ with open(path, encoding="utf-8") as f:
45
+ return json.load(f)
46
+ except (OSError, ValueError):
47
+ return None
48
+
49
+
50
+ def _detect_node(path, folder):
51
+ package = _read_json(os.path.join(path, "package.json"))
52
+ if package is None:
53
+ return None
54
+ deps = {**package.get("dependencies", {}), **package.get("devDependencies", {})}
55
+ scripts = package.get("scripts", {})
56
+ if "next" in deps:
57
+ kind = "nextjs"
58
+ elif "vite" in deps:
59
+ kind = "vite"
60
+ elif scripts.get("dev") or scripts.get("start"):
61
+ kind = "node"
62
+ else:
63
+ return None # a library or tooling package, not something to run
64
+ app = Detected(folder, kind)
65
+ for lock, manager in (("pnpm-lock.yaml", "pnpm"), ("yarn.lock", "yarn"), ("bun.lockb", "bun"), ("bun.lock", "bun")):
66
+ if os.path.exists(os.path.join(path, lock)):
67
+ app.manager = manager
68
+ break
69
+ app.script = "dev" if "dev" in scripts else "start"
70
+ script = scripts.get(app.script, "")
71
+ match = re.search(r"(?:--port|-p)[ =](\d{2,5})\b", script)
72
+ if match:
73
+ app.port, app.port_fixed = int(match.group(1)), True
74
+ elif kind == "vite":
75
+ for name in glob.glob(os.path.join(path, "vite.config.*")):
76
+ try:
77
+ text = open(name, encoding="utf-8").read()
78
+ except OSError:
79
+ continue
80
+ match = re.search(r"\bport\s*:\s*(\d{2,5})", text)
81
+ if match:
82
+ app.port, app.port_fixed = int(match.group(1)), True
83
+ if app.port is None:
84
+ app.port = DEFAULT_PORTS[kind]
85
+ if not os.path.isdir(os.path.join(path, "node_modules")):
86
+ app.notes.append("dependencies not installed")
87
+ return app
88
+
89
+
90
+ def _conda_envs():
91
+ """{name: path} of the conda environments on this machine (without running conda)."""
92
+ import shutil
93
+ from ntc.templates.drf_structure.main import find_conda_executable
94
+ home = os.path.expanduser("~")
95
+ roots = {os.path.join(home, ".conda", "envs")}
96
+ for base in ("anaconda3", "miniconda3", "miniforge3", "mambaforge"):
97
+ roots.add(os.path.join(home, base, "envs"))
98
+ roots.add(os.path.join(os.environ.get("PROGRAMDATA", r"C:\ProgramData"), base, "envs"))
99
+ for conda in (os.environ.get("CONDA_EXE"), shutil.which(find_conda_executable() or "conda")):
100
+ if conda:
101
+ # ...\anaconda3\Scripts\conda.exe, ...\anaconda3\condabin\conda.bat or .../bin/conda
102
+ roots.add(os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(conda))), "envs"))
103
+ envs = {}
104
+ for root in roots:
105
+ for path in glob.glob(os.path.join(root, "*")):
106
+ if os.path.isdir(os.path.join(path, "conda-meta")):
107
+ envs[os.path.basename(path)] = path
108
+ return envs
109
+
110
+
111
+ def _words(text):
112
+ return {w for w in re.split(r"[^a-z0-9]+", text.lower()) if len(w) > 2} - {"backend", "api", "server", "app", "env"}
113
+
114
+
115
+ def _detect_python(path, folder, workspace_name, package):
116
+ """The candidates for the app's Python, best first: [(path, kind, description)]."""
117
+ options = []
118
+ for name in ("env", "venv", ".venv"):
119
+ candidate = os.path.join(path, name)
120
+ if os.path.exists(os.path.join(candidate, "pyvenv.cfg")):
121
+ options.append((name, "venv", f"{name}/ in the app"))
122
+ elif os.path.isdir(os.path.join(candidate, "conda-meta")):
123
+ options.append((name, "conda", f"conda env {name}/ in the app"))
124
+ # A conda environment named after the project (longuo for longuo-backend / longuo_backend).
125
+ wanted = _words(folder) | _words(workspace_name) | _words(package or "")
126
+ exact = {folder.lower(), (package or "").lower(), workspace_name.lower()}
127
+ scored = []
128
+ for name, env_path in _conda_envs().items():
129
+ score = 2 if name.lower() in exact else len(_words(name) & wanted)
130
+ if score:
131
+ scored.append((score, name, env_path))
132
+ for _, name, env_path in sorted(scored, reverse=True):
133
+ options.append((env_path, "conda", f"conda env '{name}'"))
134
+ for variable, kind in (("VIRTUAL_ENV", "venv"), ("CONDA_PREFIX", "conda")):
135
+ active = os.environ.get(variable)
136
+ if active and not any(o[0] == active for o in options):
137
+ options.append((active, kind, f"the active environment ({os.path.basename(active)})"))
138
+ options.append(("env", "venv", "a new venv, env/ in the app (`ntc setup` creates it)"))
139
+ return options
140
+
141
+
142
+ def detect_app(path, folder, workspace_name):
143
+ if os.path.exists(os.path.join(path, "manage.py")):
144
+ app = Detected(folder, "django")
145
+ app.port = DEFAULT_PORTS["django"]
146
+ from ntc.workspace.setup import django_package
147
+ package = django_package(path)
148
+ app.package = package
149
+ app.python_options = _detect_python(path, folder, workspace_name, package)
150
+ app.python = app.python_options[0]
151
+ app.requirements = next((r for r in ("requirements-dev.txt", "requirements.txt")
152
+ if os.path.exists(os.path.join(path, r))), None)
153
+ if app.python[2].startswith("a new venv"):
154
+ app.notes.append("no Python environment yet")
155
+ return app
156
+ if os.path.exists(os.path.join(path, "pubspec.yaml")):
157
+ app = Detected(folder, "flutter")
158
+ return app
159
+ return _detect_node(path, folder)
160
+
161
+
162
+ def detect(root):
163
+ """The apps in root: its subfolders that are apps, or root itself when it is one."""
164
+ name = os.path.basename(os.path.abspath(root))
165
+ apps, skipped = [], []
166
+ for entry in sorted(os.listdir(root)):
167
+ path = os.path.join(root, entry)
168
+ if not os.path.isdir(path) or entry.startswith(".") or entry in ("node_modules", "env", "venv"):
169
+ continue
170
+ app = detect_app(path, entry, name)
171
+ (apps.append(app) if app else skipped.append(entry))
172
+ if not apps:
173
+ app = detect_app(root, ".", name)
174
+ if app:
175
+ app.name = name
176
+ apps.append(app)
177
+ skipped = []
178
+ _short_names(apps)
179
+ _spread_ports(apps)
180
+ return apps, skipped
181
+
182
+
183
+ def _short_names(apps):
184
+ """longuo-backend, longuo-crm ... -> backend, crm: the shared prefix says nothing."""
185
+ names = [a.folder for a in apps if a.folder != "."]
186
+ if len(names) < 2:
187
+ return
188
+ prefix = os.path.commonprefix(names)
189
+ cut = max(prefix.rfind("-"), prefix.rfind("_"))
190
+ if cut <= 0:
191
+ return
192
+ short = [n[cut + 1:] for n in names]
193
+ if all(short) and len(set(short)) == len(short):
194
+ for app in apps:
195
+ if app.folder != ".":
196
+ app.name = app.folder[cut + 1:]
197
+
198
+
199
+ def _spread_ports(apps):
200
+ """Two apps on one port can't both run: move the ones whose port isn't fixed by their script."""
201
+ taken = {a.port for a in apps if a.port and a.port_fixed}
202
+ for app in sorted(apps, key=lambda a: not a.port_fixed):
203
+ if not app.port or app.port_fixed:
204
+ continue
205
+ port = app.port
206
+ while port in taken:
207
+ port += 1
208
+ if port != app.port:
209
+ app.notes.append(f"port {app.port} is taken by another app: runs on {port}")
210
+ app.port = port
211
+ app.port_moved = True
212
+ taken.add(port)
213
+
214
+
215
+ # ── ntc.toml ────────────────────────────────────────────────────────────
216
+
217
+ def _run_command(app):
218
+ manager = app.manager
219
+ run = f"{manager} run {app.script}" if manager != "npm" or app.script != "start" else "npm start"
220
+ if getattr(app, "port_moved", False):
221
+ flag = "--port {port}"
222
+ run += f" -- {flag}" if manager == "npm" else f" {flag}"
223
+ return run
224
+
225
+
226
+ def entry(app, backend):
227
+ data = {"kind": app.kind, "path": app.folder}
228
+ if app.port:
229
+ data["port"] = app.port
230
+ if app.kind == "django":
231
+ python, python_kind, _ = app.python
232
+ data["python"] = python
233
+ if python_kind == "conda":
234
+ data["python_kind"] = "conda"
235
+ # No `migrate`: an existing project's .env may point at a shared or production database.
236
+ data["setup"] = [f"{{python}} -m pip install -r {app.requirements}"] if app.requirements else []
237
+ data["run"] = "{python} manage.py runserver 127.0.0.1:{port}"
238
+ data["package"] = app.package or "config" # for the commented worker example only
239
+ return data
240
+ if app.kind == "flutter":
241
+ data["setup"] = ["flutter pub get"]
242
+ data["run"] = "flutter run"
243
+ if backend:
244
+ data["needs"] = [backend]
245
+ return data
246
+ if backend:
247
+ data["needs"] = [backend]
248
+ install = {"npm": "npm install --no-audit --no-fund", "pnpm": "pnpm install", "yarn": "yarn install",
249
+ "bun": "bun install"}[app.manager]
250
+ data["setup"] = [install]
251
+ data["run"] = _run_command(app)
252
+ return data
253
+
254
+
255
+ def run(path=None, assume_yes=False, dry_run=False):
256
+ root = os.path.abspath(path or os.getcwd())
257
+ print(f"\n{Style.BRIGHT}ntc adopt{RESET} {DIM}— {root}{RESET}\n")
258
+ if os.path.exists(os.path.join(root, config.FILENAME)):
259
+ prompts.failure(f"{config.FILENAME} already exists here: this is a workspace. `ntc add` adds apps to it.")
260
+ return False
261
+ started = time.monotonic()
262
+ apps, skipped = detect(root)
263
+ if not apps:
264
+ prompts.failure("No apps found here (looked for manage.py, package.json with a dev/start script, pubspec.yaml).")
265
+ return False
266
+
267
+ width = max(len(a.name) for a in apps)
268
+ folder_width = max(len(a.folder) for a in apps)
269
+ print(f"Found {len(apps)} app{'s' if len(apps) != 1 else ''}:\n")
270
+ for app in apps:
271
+ port = f":{app.port}" if app.port else ""
272
+ extra = app.python[2] if app.kind == "django" else app.manager
273
+ print(f" {prompts.CHECK} {Style.BRIGHT}{app.name.ljust(width)}{RESET} {DIM}{app.folder.ljust(folder_width)}{RESET} "
274
+ f"{config.KINDS[app.kind]['label']:<18} {Fore.CYAN}{port:<6}{RESET} {DIM}{extra}{RESET}")
275
+ for note in app.notes:
276
+ print(f" {DIM}· {note}{RESET}")
277
+ if skipped:
278
+ print(f"\n {DIM}Not apps, left out: {', '.join(skipped)}{RESET}")
279
+ print()
280
+
281
+ try:
282
+ for app in apps:
283
+ if app.kind == "django" and len(app.python_options) > 1 and not assume_yes:
284
+ choice = prompts.select(f"Python for {app.name}", [
285
+ (description, i, "") for i, (_, _, description) in enumerate(app.python_options)])
286
+ app.python = app.python_options[choice]
287
+ if dry_run:
288
+ prompts.info("Dry run - nothing was written.")
289
+ return True
290
+ if not assume_yes and not prompts.confirm(f"Write {config.FILENAME}? (nothing in the apps changes)", default=True):
291
+ prompts.info("Nothing was written.")
292
+ return True
293
+ except prompts.Cancelled:
294
+ print(f"\n{DIM}Nothing was written.{RESET}")
295
+ return True
296
+
297
+ backend = next((a.name for a in apps if a.kind == "django"), None)
298
+ entries = {app.name: entry(app, backend) for app in apps}
299
+ name = os.path.basename(root)
300
+ config.write(root, name, entries)
301
+ has_root_git = os.path.isdir(os.path.join(root, ".git"))
302
+ git = "single" if has_root_git else ("per-app" if all(
303
+ os.path.isdir(os.path.join(root, a.folder, ".git")) for a in apps) else "none")
304
+ from ntc.workspace.create import _set_top_level
305
+ _set_top_level(root, "git", git)
306
+ prompts.success(f"{config.FILENAME} written {DIM}({time.monotonic() - started:.1f}s) - edit it to change a port or a command{RESET}")
307
+
308
+ needs_setup = [a.name for a in apps if a.notes and any("not installed" in n or "no Python" in n for n in a.notes)]
309
+ print(f"\n{Style.BRIGHT}Next:{RESET}")
310
+ if needs_setup:
311
+ print(f" {Fore.CYAN}ntc setup{RESET} {DIM}# installs {', '.join(needs_setup)}{RESET}")
312
+ print(f" {Fore.CYAN}ntc start{RESET}\n")
313
+ if backend:
314
+ prompts.info(f"{backend}: setup installs its packages but doesn't migrate - run migrations yourself "
315
+ f"when its .env points where you expect.")
316
+ return True