tina4-python 3.10.32__tar.gz → 3.10.38__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 (144) hide show
  1. {tina4_python-3.10.32 → tina4_python-3.10.38}/.gitignore +0 -3
  2. {tina4_python-3.10.32 → tina4_python-3.10.38}/PKG-INFO +1 -1
  3. {tina4_python-3.10.32 → tina4_python-3.10.38}/pyproject.toml +1 -1
  4. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/__init__.py +1 -1
  5. tina4_python-3.10.38/tina4_python/ai/__init__.py +312 -0
  6. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/cli/__init__.py +9 -32
  7. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/dev_admin/__init__.py +371 -4
  8. tina4_python-3.10.38/tina4_python/dev_admin/metrics.py +513 -0
  9. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/frond/engine.py +28 -4
  10. tina4_python-3.10.32/tina4_python/ai/__init__.py +0 -412
  11. {tina4_python-3.10.32 → tina4_python-3.10.38}/README.md +0 -0
  12. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/CLAUDE.md +0 -0
  13. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/HtmlElement.py +0 -0
  14. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/Testing.py +0 -0
  15. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/api/__init__.py +0 -0
  16. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/auth/__init__.py +0 -0
  17. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/cache/__init__.py +0 -0
  18. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/container/__init__.py +0 -0
  19. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/core/__init__.py +0 -0
  20. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/core/cache.py +0 -0
  21. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/core/constants.py +0 -0
  22. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/core/events.py +0 -0
  23. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/core/middleware.py +0 -0
  24. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/core/request.py +0 -0
  25. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/core/response.py +0 -0
  26. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/core/router.py +0 -0
  27. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/core/server.py +0 -0
  28. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/crud/__init__.py +0 -0
  29. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/database/__init__.py +0 -0
  30. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/database/adapter.py +0 -0
  31. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/database/connection.py +0 -0
  32. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/database/firebird.py +0 -0
  33. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/database/mssql.py +0 -0
  34. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/database/mysql.py +0 -0
  35. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/database/odbc.py +0 -0
  36. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/database/postgres.py +0 -0
  37. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/database/sqlite.py +0 -0
  38. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/debug/__init__.py +0 -0
  39. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/debug/error_overlay.py +0 -0
  40. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/dev_reload.py +0 -0
  41. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/dotenv/__init__.py +0 -0
  42. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/frond/FROND.md +0 -0
  43. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/frond/__init__.py +0 -0
  44. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/gallery/auth/meta.json +0 -0
  45. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/gallery/auth/src/routes/api/gallery_auth.py +0 -0
  46. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/gallery/database/meta.json +0 -0
  47. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/gallery/database/src/routes/api/gallery_db.py +0 -0
  48. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/gallery/error-overlay/meta.json +0 -0
  49. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/gallery/error-overlay/src/routes/api/gallery_crash.py +0 -0
  50. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/gallery/orm/meta.json +0 -0
  51. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/gallery/orm/src/orm/Product.py +0 -0
  52. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/gallery/orm/src/routes/api/gallery_products.py +0 -0
  53. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/gallery/queue/meta.json +0 -0
  54. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/gallery/queue/src/routes/api/gallery_queue.py +0 -0
  55. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/gallery/rest-api/meta.json +0 -0
  56. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/gallery/rest-api/src/routes/api/gallery_hello.py +0 -0
  57. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/gallery/templates/meta.json +0 -0
  58. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/gallery/templates/src/routes/gallery_page.py +0 -0
  59. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/gallery/templates/src/templates/gallery_page.twig +0 -0
  60. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/graphql/__init__.py +0 -0
  61. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/i18n/__init__.py +0 -0
  62. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/mcp/__init__.py +0 -0
  63. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/mcp/protocol.py +0 -0
  64. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/mcp/tools.py +0 -0
  65. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/messenger/__init__.py +0 -0
  66. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/migration/__init__.py +0 -0
  67. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/migration/runner.py +0 -0
  68. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/orm/__init__.py +0 -0
  69. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/orm/fields.py +0 -0
  70. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/orm/model.py +0 -0
  71. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/public/css/tina4.css +0 -0
  72. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/public/css/tina4.min.css +0 -0
  73. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/public/favicon.ico +0 -0
  74. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/public/images/logo.svg +0 -0
  75. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/public/images/tina4-logo-icon.webp +0 -0
  76. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/public/js/frond.min.js +0 -0
  77. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/public/js/tina4-dev-admin.min.js +0 -0
  78. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/public/js/tina4.min.js +0 -0
  79. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/public/js/tina4js.min.js +0 -0
  80. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/public/swagger/index.html +0 -0
  81. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/public/swagger/oauth2-redirect.html +0 -0
  82. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/query_builder/__init__.py +0 -0
  83. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/queue/__init__.py +0 -0
  84. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/queue_backends/__init__.py +0 -0
  85. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/queue_backends/kafka_backend.py +0 -0
  86. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/queue_backends/mongo_backend.py +0 -0
  87. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/queue_backends/rabbitmq_backend.py +0 -0
  88. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/scss/__init__.py +0 -0
  89. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/scss/tina4css/_alerts.scss +0 -0
  90. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/scss/tina4css/_badges.scss +0 -0
  91. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/scss/tina4css/_buttons.scss +0 -0
  92. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/scss/tina4css/_cards.scss +0 -0
  93. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/scss/tina4css/_forms.scss +0 -0
  94. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/scss/tina4css/_grid.scss +0 -0
  95. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/scss/tina4css/_modals.scss +0 -0
  96. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/scss/tina4css/_nav.scss +0 -0
  97. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/scss/tina4css/_reset.scss +0 -0
  98. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/scss/tina4css/_tables.scss +0 -0
  99. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/scss/tina4css/_typography.scss +0 -0
  100. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/scss/tina4css/_utilities.scss +0 -0
  101. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/scss/tina4css/_variables.scss +0 -0
  102. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/scss/tina4css/base.scss +0 -0
  103. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/scss/tina4css/colors.scss +0 -0
  104. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/scss/tina4css/tina4.scss +0 -0
  105. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/seeder/__init__.py +0 -0
  106. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/service/__init__.py +0 -0
  107. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/session/__init__.py +0 -0
  108. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/session_handlers/__init__.py +0 -0
  109. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/session_handlers/mongodb_handler.py +0 -0
  110. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/session_handlers/redis_handler.py +0 -0
  111. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/session_handlers/valkey_handler.py +0 -0
  112. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/swagger/__init__.py +0 -0
  113. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/templates/components/crud.twig +0 -0
  114. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/templates/docker/distroless/Dockerfile +0 -0
  115. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/templates/docker/poetry/Dockerfile +0 -0
  116. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/templates/docker/python/Dockerfile +0 -0
  117. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/templates/docker/uv/Dockerfile +0 -0
  118. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/templates/errors/302.twig +0 -0
  119. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/templates/errors/401.twig +0 -0
  120. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/templates/errors/403.twig +0 -0
  121. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/templates/errors/404.twig +0 -0
  122. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/templates/errors/500.twig +0 -0
  123. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/templates/errors/502.twig +0 -0
  124. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/templates/errors/503.twig +0 -0
  125. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/templates/errors/base.twig +0 -0
  126. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/templates/frontend/README.md +0 -0
  127. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/templates/readme.md +0 -0
  128. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/test_client/__init__.py +0 -0
  129. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/translations/af/LC_MESSAGES/messages.mo +0 -0
  130. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/translations/af/LC_MESSAGES/messages.po +0 -0
  131. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/translations/en/LC_MESSAGES/messages.mo +0 -0
  132. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/translations/en/LC_MESSAGES/messages.po +0 -0
  133. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/translations/es/LC_MESSAGES/messages.mo +0 -0
  134. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/translations/es/LC_MESSAGES/messages.po +0 -0
  135. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/translations/fr/LC_MESSAGES/messages.mo +0 -0
  136. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/translations/fr/LC_MESSAGES/messages.po +0 -0
  137. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/translations/ja/LC_MESSAGES/messages.mo +0 -0
  138. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/translations/ja/LC_MESSAGES/messages.po +0 -0
  139. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/translations/zh/LC_MESSAGES/messages.mo +0 -0
  140. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/translations/zh/LC_MESSAGES/messages.po +0 -0
  141. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/validator/__init__.py +0 -0
  142. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/websocket/__init__.py +0 -0
  143. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/websocket/backplane.py +0 -0
  144. {tina4_python-3.10.32 → tina4_python-3.10.38}/tina4_python/wsdl/__init__.py +0 -0
@@ -60,9 +60,6 @@ __pycache__/
60
60
  *.so
61
61
  build/
62
62
  .pytest_cache/
63
- demo/.env
64
63
  example/.env
65
- demo/data/*.db
66
- demo/logs/*.log
67
64
  .claude/settings.local.json
68
65
  .claude/worktrees/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tina4-python
3
- Version: 3.10.32
3
+ Version: 3.10.38
4
4
  Summary: Tina4 Python v3 — Zero-dependency, lightweight web framework
5
5
  Author-email: Andre van Zuydam <andrevanzuydam@gmail.com>
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "tina4-python"
3
- version = "3.10.32"
3
+ version = "3.10.38"
4
4
  description = "Tina4 Python v3 — Zero-dependency, lightweight web framework"
5
5
  authors = [
6
6
  {name = "Andre van Zuydam", email = "andrevanzuydam@gmail.com"}
@@ -8,7 +8,7 @@ Tina4 Python v3.0 — Zero-dependency, lightweight web framework.
8
8
 
9
9
  One import, everything works.
10
10
  """
11
- __version__ = "3.10.24"
11
+ __version__ = "3.10.38"
12
12
 
13
13
  # ── HTTP Constants ──
14
14
  from tina4_python.core.constants import ( # noqa: E402, F401
@@ -0,0 +1,312 @@
1
+ # Tina4 AI — Install AI coding assistant context files.
2
+ """
3
+ Simple menu-driven installer for AI tool context files.
4
+ The user picks which tools they use, we install the appropriate files.
5
+
6
+ from tina4_python.ai import show_menu, install_selected
7
+ """
8
+ import os
9
+ import shutil
10
+ import subprocess
11
+ from pathlib import Path
12
+
13
+
14
+ # Ordered list of supported AI tools
15
+ AI_TOOLS = [
16
+ {"name": "claude-code", "description": "Claude Code", "context_file": "CLAUDE.md", "config_dir": ".claude"},
17
+ {"name": "cursor", "description": "Cursor", "context_file": ".cursorules", "config_dir": ".cursor"},
18
+ {"name": "copilot", "description": "GitHub Copilot", "context_file": ".github/copilot-instructions.md", "config_dir": ".github"},
19
+ {"name": "windsurf", "description": "Windsurf", "context_file": ".windsurfrules", "config_dir": None},
20
+ {"name": "aider", "description": "Aider", "context_file": "CONVENTIONS.md", "config_dir": None},
21
+ {"name": "cline", "description": "Cline", "context_file": ".clinerules", "config_dir": None},
22
+ {"name": "codex", "description": "OpenAI Codex", "context_file": "AGENTS.md", "config_dir": None},
23
+ ]
24
+
25
+
26
+ def is_installed(root: str, tool: dict) -> bool:
27
+ """Check if a tool's context file already exists."""
28
+ return (Path(root).resolve() / tool["context_file"]).exists()
29
+
30
+
31
+ def show_menu(root: str = ".") -> str:
32
+ """Print the numbered menu and return user input."""
33
+ root = str(Path(root).resolve())
34
+ green = "\033[32m"
35
+ reset = "\033[0m"
36
+
37
+ print("\n Tina4 AI Context Installer\n")
38
+ for i, tool in enumerate(AI_TOOLS, 1):
39
+ installed = is_installed(root, tool)
40
+ marker = f" {green}[installed]{reset}" if installed else ""
41
+ print(f" {i}. {tool['description']:<20s} {tool['context_file']}{marker}")
42
+
43
+ # tina4-ai tools option
44
+ tina4_ai_installed = shutil.which("mdview") is not None
45
+ marker = f" {green}[installed]{reset}" if tina4_ai_installed else ""
46
+ print(f" 8. Install tina4-ai tools (requires Python){marker}")
47
+ print()
48
+ return input(" Select (comma-separated, or 'all'): ").strip()
49
+
50
+
51
+ def install_selected(root: str, selection: str) -> list[str]:
52
+ """Install context files for the selected tools.
53
+
54
+ selection: comma-separated numbers like "1,2,3" or "all"
55
+ Returns list of created/updated file paths.
56
+ """
57
+ root_path = Path(root).resolve()
58
+ created = []
59
+
60
+ if selection.lower() == "all":
61
+ indices = list(range(len(AI_TOOLS)))
62
+ install_tina4_ai = True
63
+ else:
64
+ parts = [s.strip() for s in selection.split(",") if s.strip()]
65
+ indices = []
66
+ install_tina4_ai = False
67
+ for p in parts:
68
+ try:
69
+ n = int(p)
70
+ if n == 8:
71
+ install_tina4_ai = True
72
+ elif 1 <= n <= len(AI_TOOLS):
73
+ indices.append(n - 1)
74
+ except ValueError:
75
+ pass
76
+
77
+ context = generate_context()
78
+
79
+ for idx in indices:
80
+ tool = AI_TOOLS[idx]
81
+ files = _install_for_tool(root_path, tool, context)
82
+ created.extend(files)
83
+
84
+ if install_tina4_ai:
85
+ _install_tina4_ai()
86
+
87
+ return created
88
+
89
+
90
+ def install_all(root: str = ".") -> list[str]:
91
+ """Install context for all AI tools (non-interactive)."""
92
+ return install_selected(root, "all")
93
+
94
+
95
+ def _install_for_tool(root: Path, tool: dict, context: str) -> list[str]:
96
+ """Install context file for a single tool."""
97
+ created = []
98
+ context_path = root / tool["context_file"]
99
+
100
+ # Create directories
101
+ if tool.get("config_dir"):
102
+ (root / tool["config_dir"]).mkdir(parents=True, exist_ok=True)
103
+ context_path.parent.mkdir(parents=True, exist_ok=True)
104
+
105
+ # Always overwrite — user chose to install
106
+ context_path.write_text(context, encoding="utf-8")
107
+ action = "Updated" if context_path.exists() else "Installed"
108
+ rel = str(context_path.relative_to(root))
109
+ created.append(rel)
110
+ print(f" \033[32m✓\033[0m {action} {rel}")
111
+
112
+ # Claude-specific extras
113
+ if tool["name"] == "claude-code":
114
+ skills = _install_claude_skills(root)
115
+ created.extend(skills)
116
+
117
+ return created
118
+
119
+
120
+ def _install_tina4_ai():
121
+ """Install tina4-ai package (provides mdview for markdown viewing)."""
122
+ print(" Installing tina4-ai tools...")
123
+ for cmd in ["pip3", "pip"]:
124
+ if shutil.which(cmd):
125
+ try:
126
+ result = subprocess.run(
127
+ [cmd, "install", "--upgrade", "tina4-ai"],
128
+ capture_output=True, text=True, timeout=60,
129
+ )
130
+ if result.returncode == 0:
131
+ print(" \033[32m✓\033[0m Installed tina4-ai (mdview)")
132
+ return
133
+ else:
134
+ print(f" \033[33m!\033[0m {cmd} failed: {result.stderr.strip()[:100]}")
135
+ except (subprocess.TimeoutExpired, OSError):
136
+ continue
137
+ print(" \033[33m!\033[0m Python/pip not available — skip tina4-ai")
138
+
139
+
140
+ def _install_claude_skills(root: Path) -> list[str]:
141
+ """Copy Claude Code skill files from the framework's templates."""
142
+ created = []
143
+ commands_dir = root / ".claude" / "commands"
144
+ commands_dir.mkdir(parents=True, exist_ok=True)
145
+
146
+ pkg_dir = Path(__file__).parent.parent
147
+ source_dirs = [pkg_dir / "templates" / "ai" / "claude-commands"]
148
+
149
+ for source_dir in source_dirs:
150
+ if source_dir.is_dir():
151
+ for skill_file in source_dir.glob("*.md"):
152
+ target = commands_dir / skill_file.name
153
+ target.write_text(skill_file.read_text(encoding="utf-8"), encoding="utf-8")
154
+ rel = str(target.relative_to(root))
155
+ created.append(rel)
156
+
157
+ # Copy skill directories from framework .claude/skills/
158
+ framework_root = pkg_dir.parent
159
+ framework_skills_dir = framework_root / ".claude" / "skills"
160
+ if framework_skills_dir.is_dir():
161
+ target_skills_dir = root / ".claude" / "skills"
162
+ target_skills_dir.mkdir(parents=True, exist_ok=True)
163
+ for skill_dir in framework_skills_dir.iterdir():
164
+ if skill_dir.is_dir():
165
+ target_dir = target_skills_dir / skill_dir.name
166
+ if target_dir.exists():
167
+ shutil.rmtree(target_dir)
168
+ shutil.copytree(skill_dir, target_dir)
169
+ rel = str(target_dir.relative_to(root))
170
+ created.append(rel)
171
+ print(f" \033[32m✓\033[0m Updated {rel}")
172
+
173
+ return created
174
+
175
+
176
+ def generate_context() -> str:
177
+ """Generate the universal Tina4 context document for any AI assistant."""
178
+ return f"""# Tina4 Python — AI Context
179
+
180
+ This project uses **Tina4 Python**, a lightweight, batteries-included web framework
181
+ with zero third-party dependencies for core features.
182
+
183
+ **Documentation:** https://tina4.com
184
+
185
+ ## Quick Start
186
+
187
+ ```bash
188
+ tina4python init . # Scaffold project
189
+ tina4python serve # Start dev server on port 7145
190
+ tina4python migrate # Run database migrations
191
+ tina4python test # Run test suite
192
+ tina4python routes # List all registered routes
193
+ ```
194
+
195
+ ## Project Structure
196
+
197
+ ```
198
+ src/routes/ — Route handlers (auto-discovered, one per resource)
199
+ src/orm/ — ORM models (one per file, filename = class name)
200
+ src/templates/ — Twig/Jinja2 templates (extends base.twig)
201
+ src/app/ — Shared helpers and service classes
202
+ src/scss/ — SCSS files (auto-compiled to public/css/)
203
+ src/public/ — Static assets served at /
204
+ src/locales/ — Translation JSON files
205
+ src/seeds/ — Database seeder scripts
206
+ migrations/ — SQL migration files (sequential numbered)
207
+ tests/ — pytest test files
208
+ ```
209
+
210
+ ## Built-in Features (No External Packages Needed)
211
+
212
+ | Feature | Module | Import |
213
+ |---------|--------|--------|
214
+ | Routing | router | `from tina4_python.core.router import get, post, put, delete` |
215
+ | ORM | orm | `from tina4_python.orm import ORM, IntegerField, StringField` |
216
+ | Database | database | `from tina4_python.database import Database` |
217
+ | Templates | template | `response.render("page.twig", data)` |
218
+ | JWT Auth | auth | `from tina4_python.auth import Auth, hash_password, check_password` |
219
+ | REST API Client | api | `from tina4_python.api import Api` |
220
+ | GraphQL | graphql | `from tina4_python.graphql import GraphQL, Schema` |
221
+ | WebSocket | websocket | `from tina4_python.websocket import WebSocketServer` |
222
+ | SOAP/WSDL | wsdl | `from tina4_python.wsdl import WSDL, wsdl_operation` |
223
+ | Email (SMTP+IMAP) | messenger | `from tina4_python.messenger import Messenger` |
224
+ | Background Queue | queue | `from tina4_python.queue import Queue` |
225
+ | SCSS Compilation | scss | Auto-compiled from src/scss/ |
226
+ | Migrations | migration | `tina4python migrate` CLI command |
227
+ | Seeder | seeder | `from tina4_python.seeder import FakeData, seed_table` |
228
+ | i18n | localization | `from tina4_python.localization import Localization` |
229
+ | Swagger/OpenAPI | swagger | Auto-generated at /swagger |
230
+ | Sessions | session | `request.session.get(key)` / `.set(key, value)` |
231
+ | Middleware | middleware | `@middleware(MyMiddleware)` decorator |
232
+ | HTML Builder | html_element | `from tina4_python.html_element import HTMLElement` |
233
+ | Form Tokens | template | `{{{{ form_token() }}}}` in Twig |
234
+
235
+ ## Key Conventions
236
+
237
+ 1. **Routes return `response()`** — always use `response(data)` not `response.json()`
238
+ 2. **GET routes are public**, POST/PUT/PATCH/DELETE require auth by default
239
+ 3. **Use `@noauth()`** to make write routes public, `@secured()` to protect GET routes
240
+ 4. **Decorator order**: `@noauth/@secured` → `@description/@tags` → `@get/@post` (route decorator innermost)
241
+ 5. **Every template extends `base.twig`** — no standalone HTML pages
242
+ 6. **No inline styles** — use SCSS in `src/scss/` with CSS variables
243
+ 7. **No hardcoded colors** — use `var(--primary)`, `var(--text)`, etc.
244
+ 8. **All schema changes via migrations** — never create tables in route code
245
+ 9. **Service pattern** — complex logic goes in `src/app/` service classes, routes stay thin
246
+ 10. **Use built-in features** — never install packages for things Tina4 already provides
247
+
248
+ ## AI Workflow — Available Skills
249
+
250
+ When using an AI coding assistant with Tina4, these skills are available:
251
+
252
+ | Skill | Description |
253
+ |-------|-------------|
254
+ | `/tina4-route` | Create a new route with proper decorators and auth |
255
+ | `/tina4-orm` | Create an ORM model with migration |
256
+ | `/tina4-crud` | Generate complete CRUD (migration, ORM, routes, template, tests) |
257
+ | `/tina4-auth` | Set up JWT authentication with login/register |
258
+ | `/tina4-api` | Create an external API integration |
259
+ | `/tina4-queue` | Set up background job processing |
260
+ | `/tina4-template` | Create a server-rendered template page |
261
+ | `/tina4-graphql` | Set up a GraphQL endpoint |
262
+ | `/tina4-websocket` | Set up WebSocket communication |
263
+ | `/tina4-wsdl` | Create a SOAP/WSDL service |
264
+ | `/tina4-messenger` | Set up email send/receive |
265
+ | `/tina4-test` | Write tests for a feature |
266
+ | `/tina4-migration` | Create a database migration |
267
+ | `/tina4-seed` | Generate fake data for development |
268
+ | `/tina4-i18n` | Set up internationalization |
269
+ | `/tina4-scss` | Set up SCSS stylesheets |
270
+ | `/tina4-frontend` | Set up a frontend framework |
271
+
272
+ ## Common Patterns
273
+
274
+ ### Route
275
+ ```python
276
+ from tina4_python.core.router import get, post, noauth
277
+ from tina4_python.swagger import description, tags
278
+
279
+ @noauth()
280
+ @description("Create a widget")
281
+ @tags(["widgets"])
282
+ @post("/api/widgets")
283
+ async def create_widget(request, response):
284
+ data = request.body
285
+ return response({{"created": True}}, 201)
286
+ ```
287
+
288
+ ### ORM Model
289
+ ```python
290
+ from tina4_python.orm import ORM, IntegerField, StringField
291
+
292
+ class Widget(ORM):
293
+ id = IntegerField(primary_key=True, auto_increment=True)
294
+ name = StringField()
295
+ ```
296
+
297
+ ### Template
298
+ ```twig
299
+ {{% extends "base.twig" %}}
300
+ {{% block content %}}
301
+ <div class="container">
302
+ <h1>{{{{ title }}}}</h1>
303
+ {{% for item in items %}}
304
+ <p>{{{{ item.name }}}}</p>
305
+ {{% endfor %}}
306
+ </div>
307
+ {{% endblock %}}
308
+ ```
309
+ """
310
+
311
+
312
+ __all__ = ["AI_TOOLS", "is_installed", "show_menu", "install_selected", "install_all", "generate_context"]
@@ -405,42 +405,19 @@ def _build(args):
405
405
 
406
406
 
407
407
  def _ai(args):
408
- """Detect AI coding tools and install Tina4 context."""
409
- from tina4_python.ai import detect_ai, install_context, install_all, status_report
408
+ """Install AI coding assistant context files."""
409
+ from tina4_python.ai import show_menu, install_selected, install_all
410
410
 
411
411
  root = "."
412
412
 
413
- if "--all" in args:
414
- # Install context for ALL known AI tools
415
- created = install_all(root, force="--force" in args)
416
- if created:
417
- print("Installed Tina4 context for all AI tools:")
418
- for f in created:
419
- print(f" + {f}")
420
- else:
421
- print("All AI context files already exist. Use --force to overwrite.")
422
- elif "--status" in args or not args:
423
- # Show detection status
424
- print(status_report(root))
425
-
426
- # Auto-install for detected tools
427
- detected = [t for t in detect_ai(root) if t["installed"]]
428
- if detected:
429
- created = install_context(root)
430
- if created:
431
- print("Installed Tina4 context:")
432
- for f in created:
433
- print(f" + {f}")
434
- else:
435
- print("Context files already exist. Use --force to overwrite.")
413
+ if args and args[0].lower() == "all":
414
+ # Non-interactive: install everything
415
+ install_all(root)
436
416
  else:
437
- # Install for specific tool(s)
438
- created = install_context(root, tools=args, force="--force" in args)
439
- if created:
440
- for f in created:
441
- print(f" + {f}")
442
- else:
443
- print("Nothing to install.")
417
+ # Interactive: show menu, get selection
418
+ selection = show_menu(root)
419
+ if selection:
420
+ install_selected(root, selection)
444
421
 
445
422
 
446
423
  def _generate(args):