stkit 17.2.3__tar.gz → 17.2.5__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 (239) hide show
  1. {stkit-17.2.3 → stkit-17.2.5}/CHANGELOG.md +18 -0
  2. {stkit-17.2.3 → stkit-17.2.5}/PKG-INFO +1 -1
  3. {stkit-17.2.3 → stkit-17.2.5}/examples/minimal/minimal/app.py +2 -1
  4. {stkit-17.2.3 → stkit-17.2.5}/pyproject.toml +1 -1
  5. {stkit-17.2.3 → stkit-17.2.5}/stk/__init__.py +1 -1
  6. {stkit-17.2.3 → stkit-17.2.5}/stk/app.py +0 -2
  7. {stkit-17.2.3 → stkit-17.2.5}/stk/checks.py +22 -0
  8. {stkit-17.2.3 → stkit-17.2.5}/stk/cli/copy_rules.py +3 -1
  9. {stkit-17.2.3 → stkit-17.2.5}/stk/cli/smoke.py +12 -3
  10. {stkit-17.2.3 → stkit-17.2.5}/stk/notification/__init__.py +13 -1
  11. {stkit-17.2.3 → stkit-17.2.5}/stk/scaffold/app/__app__/app.py +2 -1
  12. {stkit-17.2.3 → stkit-17.2.5}/stk/templates/layout.html +3 -1
  13. {stkit-17.2.3 → stkit-17.2.5}/tests/test_copy_rules.py +26 -0
  14. {stkit-17.2.3 → stkit-17.2.5}/tests/test_notifications.py +2 -1
  15. stkit-17.2.5/tests/test_route_collisions.py +41 -0
  16. {stkit-17.2.3 → stkit-17.2.5}/tests/test_scaffolder.py +1 -1
  17. {stkit-17.2.3 → stkit-17.2.5}/.gitignore +0 -0
  18. {stkit-17.2.3 → stkit-17.2.5}/LICENSE +0 -0
  19. {stkit-17.2.3 → stkit-17.2.5}/README.md +0 -0
  20. {stkit-17.2.3 → stkit-17.2.5}/SECURITY.md +0 -0
  21. {stkit-17.2.3 → stkit-17.2.5}/examples/minimal/.env-sample +0 -0
  22. {stkit-17.2.3 → stkit-17.2.5}/examples/minimal/.gitignore +0 -0
  23. {stkit-17.2.3 → stkit-17.2.5}/examples/minimal/README.md +0 -0
  24. {stkit-17.2.3 → stkit-17.2.5}/examples/minimal/minimal/__init__.py +0 -0
  25. {stkit-17.2.3 → stkit-17.2.5}/examples/minimal/minimal/migrations/364232ae77d5_add_note.py +0 -0
  26. {stkit-17.2.3 → stkit-17.2.5}/examples/minimal/minimal/note/__init__.py +0 -0
  27. {stkit-17.2.3 → stkit-17.2.5}/examples/minimal/minimal/note/models.py +0 -0
  28. {stkit-17.2.3 → stkit-17.2.5}/examples/minimal/minimal/note/templates/note/index.html +0 -0
  29. {stkit-17.2.3 → stkit-17.2.5}/examples/minimal/minimal/note/views.py +0 -0
  30. {stkit-17.2.3 → stkit-17.2.5}/examples/minimal/minimal/settings.py +0 -0
  31. {stkit-17.2.3 → stkit-17.2.5}/examples/minimal/minimal/templates/layout.html +0 -0
  32. {stkit-17.2.3 → stkit-17.2.5}/examples/minimal/pyproject.toml +0 -0
  33. {stkit-17.2.3 → stkit-17.2.5}/examples/minimal/tests/__init__.py +0 -0
  34. {stkit-17.2.3 → stkit-17.2.5}/examples/minimal/tests/test_app.py +0 -0
  35. {stkit-17.2.3 → stkit-17.2.5}/examples/minimal/uv.lock +0 -0
  36. {stkit-17.2.3 → stkit-17.2.5}/stk/__main__.py +0 -0
  37. {stkit-17.2.3 → stkit-17.2.5}/stk/agent_login.py +0 -0
  38. {stkit-17.2.3 → stkit-17.2.5}/stk/cli/__init__.py +0 -0
  39. {stkit-17.2.3 → stkit-17.2.5}/stk/cli/agent.py +0 -0
  40. {stkit-17.2.3 → stkit-17.2.5}/stk/cli/base.py +0 -0
  41. {stkit-17.2.3 → stkit-17.2.5}/stk/cli/database.py +0 -0
  42. {stkit-17.2.3 → stkit-17.2.5}/stk/cli/develop.py +0 -0
  43. {stkit-17.2.3 → stkit-17.2.5}/stk/cli/doctor.py +0 -0
  44. {stkit-17.2.3 → stkit-17.2.5}/stk/cli/main.py +0 -0
  45. {stkit-17.2.3 → stkit-17.2.5}/stk/cli/reports.py +0 -0
  46. {stkit-17.2.3 → stkit-17.2.5}/stk/cli/users.py +0 -0
  47. {stkit-17.2.3 → stkit-17.2.5}/stk/cli/watch.py +0 -0
  48. {stkit-17.2.3 → stkit-17.2.5}/stk/extensions.py +0 -0
  49. {stkit-17.2.3 → stkit-17.2.5}/stk/files/__init__.py +0 -0
  50. {stkit-17.2.3 → stkit-17.2.5}/stk/files/models.py +0 -0
  51. {stkit-17.2.3 → stkit-17.2.5}/stk/files/store.py +0 -0
  52. {stkit-17.2.3 → stkit-17.2.5}/stk/files/views.py +0 -0
  53. {stkit-17.2.3 → stkit-17.2.5}/stk/mcp/__init__.py +0 -0
  54. {stkit-17.2.3 → stkit-17.2.5}/stk/mcp/models.py +0 -0
  55. {stkit-17.2.3 → stkit-17.2.5}/stk/mcp/server.py +0 -0
  56. {stkit-17.2.3 → stkit-17.2.5}/stk/mcp/views.py +0 -0
  57. {stkit-17.2.3 → stkit-17.2.5}/stk/migrations/__init__.py +0 -0
  58. {stkit-17.2.3 → stkit-17.2.5}/stk/migrations/env.py +0 -0
  59. {stkit-17.2.3 → stkit-17.2.5}/stk/migrations/script.py.mako +0 -0
  60. {stkit-17.2.3 → stkit-17.2.5}/stk/migrations/versions/stk_0001_initial_schema.py +0 -0
  61. {stkit-17.2.3 → stkit-17.2.5}/stk/migrations/versions/stk_0002_jobs.py +0 -0
  62. {stkit-17.2.3 → stkit-17.2.5}/stk/migrations/versions/stk_0003_files.py +0 -0
  63. {stkit-17.2.3 → stkit-17.2.5}/stk/migrations/versions/stk_0004_notifications.py +0 -0
  64. {stkit-17.2.3 → stkit-17.2.5}/stk/migrations/versions/stk_0005_mcp_tokens.py +0 -0
  65. {stkit-17.2.3 → stkit-17.2.5}/stk/migrations/versions/stk_0006_stk_mcp_token.py +0 -0
  66. {stkit-17.2.3 → stkit-17.2.5}/stk/notification/models.py +0 -0
  67. {stkit-17.2.3 → stkit-17.2.5}/stk/notification/views.py +0 -0
  68. {stkit-17.2.3 → stkit-17.2.5}/stk/portal/views.py +0 -0
  69. {stkit-17.2.3 → stkit-17.2.5}/stk/public/__init__.py +0 -0
  70. {stkit-17.2.3 → stkit-17.2.5}/stk/public/models.py +0 -0
  71. {stkit-17.2.3 → stkit-17.2.5}/stk/public/views.py +0 -0
  72. {stkit-17.2.3 → stkit-17.2.5}/stk/scaffold/__init__.py +0 -0
  73. {stkit-17.2.3 → stkit-17.2.5}/stk/scaffold/app/CLAUDE.md +0 -0
  74. {stkit-17.2.3 → stkit-17.2.5}/stk/scaffold/app/Dockerfile +0 -0
  75. {stkit-17.2.3 → stkit-17.2.5}/stk/scaffold/app/README.md +0 -0
  76. {stkit-17.2.3 → stkit-17.2.5}/stk/scaffold/app/__app__/__init__.py +0 -0
  77. {stkit-17.2.3 → stkit-17.2.5}/stk/scaffold/app/__app__/settings.py +0 -0
  78. {stkit-17.2.3 → stkit-17.2.5}/stk/scaffold/app/claude/skills/stk-api/SKILL.md +0 -0
  79. {stkit-17.2.3 → stkit-17.2.5}/stk/scaffold/app/claude/skills/stk-blueprint/SKILL.md +0 -0
  80. {stkit-17.2.3 → stkit-17.2.5}/stk/scaffold/app/claude/skills/stk-deploy/SKILL.md +0 -0
  81. {stkit-17.2.3 → stkit-17.2.5}/stk/scaffold/app/claude/skills/stk-lessons/SKILL.md +0 -0
  82. {stkit-17.2.3 → stkit-17.2.5}/stk/scaffold/app/claude/skills/stk-migrate/SKILL.md +0 -0
  83. {stkit-17.2.3 → stkit-17.2.5}/stk/scaffold/app/docker-compose.yml +0 -0
  84. {stkit-17.2.3 → stkit-17.2.5}/stk/scaffold/app/dockerignore +0 -0
  85. {stkit-17.2.3 → stkit-17.2.5}/stk/scaffold/app/env-sample +0 -0
  86. {stkit-17.2.3 → stkit-17.2.5}/stk/scaffold/app/github/workflows/checks.yml +0 -0
  87. {stkit-17.2.3 → stkit-17.2.5}/stk/scaffold/app/gitignore +0 -0
  88. {stkit-17.2.3 → stkit-17.2.5}/stk/scaffold/app/nginx/nginx.conf +0 -0
  89. {stkit-17.2.3 → stkit-17.2.5}/stk/scaffold/app/nginx/stk.conf +0 -0
  90. {stkit-17.2.3 → stkit-17.2.5}/stk/scaffold/app/postgres/init-db.sh +0 -0
  91. {stkit-17.2.3 → stkit-17.2.5}/stk/scaffold/app/pyproject.toml +0 -0
  92. {stkit-17.2.3 → stkit-17.2.5}/stk/scaffold/app/tests/__init__.py +0 -0
  93. {stkit-17.2.3 → stkit-17.2.5}/stk/scaffold/app/tests/test_app.py +0 -0
  94. {stkit-17.2.3 → stkit-17.2.5}/stk/scaffold/generator.py +0 -0
  95. {stkit-17.2.3 → stkit-17.2.5}/stk/scaffold/templates.py +0 -0
  96. {stkit-17.2.3 → stkit-17.2.5}/stk/security.py +0 -0
  97. {stkit-17.2.3 → stkit-17.2.5}/stk/settings.py +0 -0
  98. {stkit-17.2.3 → stkit-17.2.5}/stk/shell.py +0 -0
  99. {stkit-17.2.3 → stkit-17.2.5}/stk/static/VERSIONS.txt +0 -0
  100. {stkit-17.2.3 → stkit-17.2.5}/stk/static/css/app.css +0 -0
  101. {stkit-17.2.3 → stkit-17.2.5}/stk/static/css/layout.css +0 -0
  102. {stkit-17.2.3 → stkit-17.2.5}/stk/static/css/vuetify.min.css +0 -0
  103. {stkit-17.2.3 → stkit-17.2.5}/stk/static/icons/fonts/tabler-icons.animals.woff2 +0 -0
  104. {stkit-17.2.3 → stkit-17.2.5}/stk/static/icons/fonts/tabler-icons.arrows-1.woff2 +0 -0
  105. {stkit-17.2.3 → stkit-17.2.5}/stk/static/icons/fonts/tabler-icons.arrows-2.woff2 +0 -0
  106. {stkit-17.2.3 → stkit-17.2.5}/stk/static/icons/fonts/tabler-icons.badges.woff2 +0 -0
  107. {stkit-17.2.3 → stkit-17.2.5}/stk/static/icons/fonts/tabler-icons.brand-1.woff2 +0 -0
  108. {stkit-17.2.3 → stkit-17.2.5}/stk/static/icons/fonts/tabler-icons.brand-2.woff2 +0 -0
  109. {stkit-17.2.3 → stkit-17.2.5}/stk/static/icons/fonts/tabler-icons.buildings.woff2 +0 -0
  110. {stkit-17.2.3 → stkit-17.2.5}/stk/static/icons/fonts/tabler-icons.charts.woff2 +0 -0
  111. {stkit-17.2.3 → stkit-17.2.5}/stk/static/icons/fonts/tabler-icons.communication.woff2 +0 -0
  112. {stkit-17.2.3 → stkit-17.2.5}/stk/static/icons/fonts/tabler-icons.computers.woff2 +0 -0
  113. {stkit-17.2.3 → stkit-17.2.5}/stk/static/icons/fonts/tabler-icons.core.woff2 +0 -0
  114. {stkit-17.2.3 → stkit-17.2.5}/stk/static/icons/fonts/tabler-icons.currencies.woff2 +0 -0
  115. {stkit-17.2.3 → stkit-17.2.5}/stk/static/icons/fonts/tabler-icons.database.woff2 +0 -0
  116. {stkit-17.2.3 → stkit-17.2.5}/stk/static/icons/fonts/tabler-icons.design-1.woff2 +0 -0
  117. {stkit-17.2.3 → stkit-17.2.5}/stk/static/icons/fonts/tabler-icons.design-2.woff2 +0 -0
  118. {stkit-17.2.3 → stkit-17.2.5}/stk/static/icons/fonts/tabler-icons.development.woff2 +0 -0
  119. {stkit-17.2.3 → stkit-17.2.5}/stk/static/icons/fonts/tabler-icons.devices-1.woff2 +0 -0
  120. {stkit-17.2.3 → stkit-17.2.5}/stk/static/icons/fonts/tabler-icons.devices-2.woff2 +0 -0
  121. {stkit-17.2.3 → stkit-17.2.5}/stk/static/icons/fonts/tabler-icons.document.woff2 +0 -0
  122. {stkit-17.2.3 → stkit-17.2.5}/stk/static/icons/fonts/tabler-icons.e-commerce.woff2 +0 -0
  123. {stkit-17.2.3 → stkit-17.2.5}/stk/static/icons/fonts/tabler-icons.electrical.woff2 +0 -0
  124. {stkit-17.2.3 → stkit-17.2.5}/stk/static/icons/fonts/tabler-icons.extensions.woff2 +0 -0
  125. {stkit-17.2.3 → stkit-17.2.5}/stk/static/icons/fonts/tabler-icons.food.woff2 +0 -0
  126. {stkit-17.2.3 → stkit-17.2.5}/stk/static/icons/fonts/tabler-icons.games.woff2 +0 -0
  127. {stkit-17.2.3 → stkit-17.2.5}/stk/static/icons/fonts/tabler-icons.gender.woff2 +0 -0
  128. {stkit-17.2.3 → stkit-17.2.5}/stk/static/icons/fonts/tabler-icons.gestures.woff2 +0 -0
  129. {stkit-17.2.3 → stkit-17.2.5}/stk/static/icons/fonts/tabler-icons.health.woff2 +0 -0
  130. {stkit-17.2.3 → stkit-17.2.5}/stk/static/icons/fonts/tabler-icons.laundry.woff2 +0 -0
  131. {stkit-17.2.3 → stkit-17.2.5}/stk/static/icons/fonts/tabler-icons.letters.woff2 +0 -0
  132. {stkit-17.2.3 → stkit-17.2.5}/stk/static/icons/fonts/tabler-icons.logic.woff2 +0 -0
  133. {stkit-17.2.3 → stkit-17.2.5}/stk/static/icons/fonts/tabler-icons.map.woff2 +0 -0
  134. {stkit-17.2.3 → stkit-17.2.5}/stk/static/icons/fonts/tabler-icons.math.woff2 +0 -0
  135. {stkit-17.2.3 → stkit-17.2.5}/stk/static/icons/fonts/tabler-icons.media.woff2 +0 -0
  136. {stkit-17.2.3 → stkit-17.2.5}/stk/static/icons/fonts/tabler-icons.mood.woff2 +0 -0
  137. {stkit-17.2.3 → stkit-17.2.5}/stk/static/icons/fonts/tabler-icons.nature.woff2 +0 -0
  138. {stkit-17.2.3 → stkit-17.2.5}/stk/static/icons/fonts/tabler-icons.numbers.woff2 +0 -0
  139. {stkit-17.2.3 → stkit-17.2.5}/stk/static/icons/fonts/tabler-icons.photography.woff2 +0 -0
  140. {stkit-17.2.3 → stkit-17.2.5}/stk/static/icons/fonts/tabler-icons.shapes.woff2 +0 -0
  141. {stkit-17.2.3 → stkit-17.2.5}/stk/static/icons/fonts/tabler-icons.sport.woff2 +0 -0
  142. {stkit-17.2.3 → stkit-17.2.5}/stk/static/icons/fonts/tabler-icons.symbols.woff2 +0 -0
  143. {stkit-17.2.3 → stkit-17.2.5}/stk/static/icons/fonts/tabler-icons.system-1.woff2 +0 -0
  144. {stkit-17.2.3 → stkit-17.2.5}/stk/static/icons/fonts/tabler-icons.system-2.woff2 +0 -0
  145. {stkit-17.2.3 → stkit-17.2.5}/stk/static/icons/fonts/tabler-icons.system-3.woff2 +0 -0
  146. {stkit-17.2.3 → stkit-17.2.5}/stk/static/icons/fonts/tabler-icons.text.woff2 +0 -0
  147. {stkit-17.2.3 → stkit-17.2.5}/stk/static/icons/fonts/tabler-icons.vehicles.woff2 +0 -0
  148. {stkit-17.2.3 → stkit-17.2.5}/stk/static/icons/fonts/tabler-icons.version-control.woff2 +0 -0
  149. {stkit-17.2.3 → stkit-17.2.5}/stk/static/icons/fonts/tabler-icons.weather.woff2 +0 -0
  150. {stkit-17.2.3 → stkit-17.2.5}/stk/static/icons/fonts/tabler-icons.zodiac.woff2 +0 -0
  151. {stkit-17.2.3 → stkit-17.2.5}/stk/static/icons/tabler-icons.min.css +0 -0
  152. {stkit-17.2.3 → stkit-17.2.5}/stk/static/img/auth-bg.webp +0 -0
  153. {stkit-17.2.3 → stkit-17.2.5}/stk/static/img/favicon.ico +0 -0
  154. {stkit-17.2.3 → stkit-17.2.5}/stk/static/img/stk.svg +0 -0
  155. {stkit-17.2.3 → stkit-17.2.5}/stk/static/js/axios.min.js +0 -0
  156. {stkit-17.2.3 → stkit-17.2.5}/stk/static/js/components/NotificationDropdown.js +0 -0
  157. {stkit-17.2.3 → stkit-17.2.5}/stk/static/js/components/ThemeSwitcher.js +0 -0
  158. {stkit-17.2.3 → stkit-17.2.5}/stk/static/js/components/TransitionExpand.js +0 -0
  159. {stkit-17.2.3 → stkit-17.2.5}/stk/static/js/components/VerticalNavGroup.js +0 -0
  160. {stkit-17.2.3 → stkit-17.2.5}/stk/static/js/components/VerticalNavLink.js +0 -0
  161. {stkit-17.2.3 → stkit-17.2.5}/stk/static/js/components/VerticalNavSectionTitle.js +0 -0
  162. {stkit-17.2.3 → stkit-17.2.5}/stk/static/js/components/index.js +0 -0
  163. {stkit-17.2.3 → stkit-17.2.5}/stk/static/js/config.js +0 -0
  164. {stkit-17.2.3 → stkit-17.2.5}/stk/static/js/vue.min.js +0 -0
  165. {stkit-17.2.3 → stkit-17.2.5}/stk/static/js/vuetify.min.js +0 -0
  166. {stkit-17.2.3 → stkit-17.2.5}/stk/static/robots.txt +0 -0
  167. {stkit-17.2.3 → stkit-17.2.5}/stk/tasks/__init__.py +0 -0
  168. {stkit-17.2.3 → stkit-17.2.5}/stk/tasks/models.py +0 -0
  169. {stkit-17.2.3 → stkit-17.2.5}/stk/templates/401.html +0 -0
  170. {stkit-17.2.3 → stkit-17.2.5}/stk/templates/404.html +0 -0
  171. {stkit-17.2.3 → stkit-17.2.5}/stk/templates/500.html +0 -0
  172. {stkit-17.2.3 → stkit-17.2.5}/stk/templates/_brand.html +0 -0
  173. {stkit-17.2.3 → stkit-17.2.5}/stk/templates/auth_layout.html +0 -0
  174. {stkit-17.2.3 → stkit-17.2.5}/stk/templates/cms/activities.html +0 -0
  175. {stkit-17.2.3 → stkit-17.2.5}/stk/templates/cms/jobs.html +0 -0
  176. {stkit-17.2.3 → stkit-17.2.5}/stk/templates/cms/roles.html +0 -0
  177. {stkit-17.2.3 → stkit-17.2.5}/stk/templates/cms/users.html +0 -0
  178. {stkit-17.2.3 → stkit-17.2.5}/stk/templates/dashboard.html +0 -0
  179. {stkit-17.2.3 → stkit-17.2.5}/stk/templates/index.html +0 -0
  180. {stkit-17.2.3 → stkit-17.2.5}/stk/templates/login-layout.html +0 -0
  181. {stkit-17.2.3 → stkit-17.2.5}/stk/templates/mcp/tokens.html +0 -0
  182. {stkit-17.2.3 → stkit-17.2.5}/stk/templates/security/_macros.html +0 -0
  183. {stkit-17.2.3 → stkit-17.2.5}/stk/templates/security/_messages.html +0 -0
  184. {stkit-17.2.3 → stkit-17.2.5}/stk/templates/security/change_password.html +0 -0
  185. {stkit-17.2.3 → stkit-17.2.5}/stk/templates/security/confirm_email.html +0 -0
  186. {stkit-17.2.3 → stkit-17.2.5}/stk/templates/security/email/change_notice.html +0 -0
  187. {stkit-17.2.3 → stkit-17.2.5}/stk/templates/security/email/change_notice.txt +0 -0
  188. {stkit-17.2.3 → stkit-17.2.5}/stk/templates/security/email/confirmation_instructions.html +0 -0
  189. {stkit-17.2.3 → stkit-17.2.5}/stk/templates/security/email/confirmation_instructions.txt +0 -0
  190. {stkit-17.2.3 → stkit-17.2.5}/stk/templates/security/email/login_instructions.html +0 -0
  191. {stkit-17.2.3 → stkit-17.2.5}/stk/templates/security/email/login_instructions.txt +0 -0
  192. {stkit-17.2.3 → stkit-17.2.5}/stk/templates/security/email/reset_instructions.html +0 -0
  193. {stkit-17.2.3 → stkit-17.2.5}/stk/templates/security/email/reset_instructions.txt +0 -0
  194. {stkit-17.2.3 → stkit-17.2.5}/stk/templates/security/email/reset_notice.html +0 -0
  195. {stkit-17.2.3 → stkit-17.2.5}/stk/templates/security/email/reset_notice.txt +0 -0
  196. {stkit-17.2.3 → stkit-17.2.5}/stk/templates/security/email/welcome.html +0 -0
  197. {stkit-17.2.3 → stkit-17.2.5}/stk/templates/security/email/welcome.txt +0 -0
  198. {stkit-17.2.3 → stkit-17.2.5}/stk/templates/security/forgot_password.html +0 -0
  199. {stkit-17.2.3 → stkit-17.2.5}/stk/templates/security/login_user.html +0 -0
  200. {stkit-17.2.3 → stkit-17.2.5}/stk/templates/security/mf_recovery.html +0 -0
  201. {stkit-17.2.3 → stkit-17.2.5}/stk/templates/security/mf_recovery_codes.html +0 -0
  202. {stkit-17.2.3 → stkit-17.2.5}/stk/templates/security/register_user.html +0 -0
  203. {stkit-17.2.3 → stkit-17.2.5}/stk/templates/security/reset_password.html +0 -0
  204. {stkit-17.2.3 → stkit-17.2.5}/stk/templates/security/send_confirmation.html +0 -0
  205. {stkit-17.2.3 → stkit-17.2.5}/stk/templates/security/send_login.html +0 -0
  206. {stkit-17.2.3 → stkit-17.2.5}/stk/templates/security/two_factor_select.html +0 -0
  207. {stkit-17.2.3 → stkit-17.2.5}/stk/templates/security/two_factor_setup.html +0 -0
  208. {stkit-17.2.3 → stkit-17.2.5}/stk/templates/security/two_factor_verify_code.html +0 -0
  209. {stkit-17.2.3 → stkit-17.2.5}/stk/templates/security/wan_register.html +0 -0
  210. {stkit-17.2.3 → stkit-17.2.5}/stk/templates/security/wan_signin.html +0 -0
  211. {stkit-17.2.3 → stkit-17.2.5}/stk/templates/security/wan_verify.html +0 -0
  212. {stkit-17.2.3 → stkit-17.2.5}/stk/user/__init__.py +0 -0
  213. {stkit-17.2.3 → stkit-17.2.5}/stk/user/forms.py +0 -0
  214. {stkit-17.2.3 → stkit-17.2.5}/stk/user/models.py +0 -0
  215. {stkit-17.2.3 → stkit-17.2.5}/stk/user/views.py +0 -0
  216. {stkit-17.2.3 → stkit-17.2.5}/stk/utils/email.py +0 -0
  217. {stkit-17.2.3 → stkit-17.2.5}/stk/utils/netguard.py +0 -0
  218. {stkit-17.2.3 → stkit-17.2.5}/stk/utils/ratelimit.py +0 -0
  219. {stkit-17.2.3 → stkit-17.2.5}/stk/websocket.py +0 -0
  220. {stkit-17.2.3 → stkit-17.2.5}/tests/test_agent_operability.py +0 -0
  221. {stkit-17.2.3 → stkit-17.2.5}/tests/test_app_index.py +0 -0
  222. {stkit-17.2.3 → stkit-17.2.5}/tests/test_branding.py +0 -0
  223. {stkit-17.2.3 → stkit-17.2.5}/tests/test_builder_ux.py +0 -0
  224. {stkit-17.2.3 → stkit-17.2.5}/tests/test_cli.py +0 -0
  225. {stkit-17.2.3 → stkit-17.2.5}/tests/test_composition.py +0 -0
  226. {stkit-17.2.3 → stkit-17.2.5}/tests/test_csrf_exempt.py +0 -0
  227. {stkit-17.2.3 → stkit-17.2.5}/tests/test_error_handler.py +0 -0
  228. {stkit-17.2.3 → stkit-17.2.5}/tests/test_files.py +0 -0
  229. {stkit-17.2.3 → stkit-17.2.5}/tests/test_jobs.py +0 -0
  230. {stkit-17.2.3 → stkit-17.2.5}/tests/test_mcp.py +0 -0
  231. {stkit-17.2.3 → stkit-17.2.5}/tests/test_mcp_migration.py +0 -0
  232. {stkit-17.2.3 → stkit-17.2.5}/tests/test_migration_drift.py +0 -0
  233. {stkit-17.2.3 → stkit-17.2.5}/tests/test_netguard.py +0 -0
  234. {stkit-17.2.3 → stkit-17.2.5}/tests/test_production_security.py +0 -0
  235. {stkit-17.2.3 → stkit-17.2.5}/tests/test_revision_imports.py +0 -0
  236. {stkit-17.2.3 → stkit-17.2.5}/tests/test_security_compatibility.py +0 -0
  237. {stkit-17.2.3 → stkit-17.2.5}/tests/test_shell.py +0 -0
  238. {stkit-17.2.3 → stkit-17.2.5}/tests/test_smoke_pages.py +0 -0
  239. {stkit-17.2.3 → stkit-17.2.5}/tests/test_static_versioning.py +0 -0
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## v17.2.5 (2026-10-31)
4
+
5
+ No migration required.
6
+
7
+ - `stk smoke` crashed on any page with three or more like SVG shapes side by side, such as a chart's paths or an icon's circles: the repeated-row rule read `innerText`, which SVG elements lack. The rule now looks at HTML elements only. A page smoke cannot read now fails that page with the reason, instead of stopping the whole run (Build Loop).
8
+
9
+ ## v17.2.4 (2026-10-31)
10
+
11
+ No migration required.
12
+
13
+ - Notifications are opt-in, like uploads and MCP: add `stk.notification` to the app's extensions for the `/api/notifications` routes and the layout's bell. From 17.2.0 to 17.2.3 stk registered those routes in every app, and because the first matching route answers, an app's own `/api/notifications` was silently never reached. `notify()` still stores rows without it (Background Work).
14
+ - `stk verify` fails when two endpoints register the same path and method, naming both, so a shadowed route can no longer ship silently.
15
+ - Apps made by `stk init`, and the example app, list `stk.notification`.
16
+
17
+ ### Upgrade considerations
18
+
19
+ - An app that uses stk's bell adds `import stk.notification` and puts `stk.notification` in its `extensions` list, or the bell disappears and `/api/notifications` returns 404.
20
+
3
21
  ## v17.2.3 (2026-10-31)
4
22
 
5
23
  Run `uv run stk db upgrade`.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: stkit
3
- Version: 17.2.3
3
+ Version: 17.2.5
4
4
  Summary: An async Quart framework optimized for AI-assisted development workflows
5
5
  Project-URL: Documentation, https://docs.stkit.dev
6
6
  Project-URL: Source, https://github.com/level09/stk
@@ -1,9 +1,10 @@
1
+ import stk.notification
1
2
  from stk import Stk
2
3
 
3
4
  from minimal import note
4
5
  from minimal.settings import Config
5
6
 
6
7
  # Each extension is a package with init_app(app); order is precedence.
7
- extensions = [note]
8
+ extensions = [stk.notification, note]
8
9
 
9
10
  app = Stk(__name__, config=Config, extensions=extensions)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "stkit"
3
- version = "17.2.3"
3
+ version = "17.2.5"
4
4
  description = "An async Quart framework optimized for AI-assisted development workflows"
5
5
  authors = [
6
6
  {name = "Level09", email = "hello@level09.com"}
@@ -3,7 +3,7 @@ stk Framework
3
3
  A modern Python web framework optimized for AI-assisted development.
4
4
  """
5
5
 
6
- __version__ = "17.2.3"
6
+ __version__ = "17.2.5"
7
7
 
8
8
 
9
9
  def __getattr__(name):
@@ -27,7 +27,6 @@ from werkzeug.security import safe_join
27
27
  import stk.extensions as ext
28
28
  from stk.agent_login import agent_login_enabled, bp_agent_login
29
29
  from stk.extensions import session
30
- from stk.notification.views import bp_notification
31
30
  from stk.portal.views import portal
32
31
  from stk.public.views import public
33
32
  from stk.settings import Config
@@ -206,7 +205,6 @@ def register_blueprints(app):
206
205
  app.register_blueprint(bp_user)
207
206
  app.register_blueprint(public)
208
207
  app.register_blueprint(portal)
209
- app.register_blueprint(bp_notification)
210
208
  app.register_blueprint(ws_bp)
211
209
  if agent_login_enabled(app):
212
210
  app.register_blueprint(bp_agent_login)
@@ -295,6 +295,28 @@ def check_admin_reset_route(app):
295
295
  )
296
296
 
297
297
 
298
+ @check("No route is registered twice")
299
+ def check_unique_routes(app):
300
+ # Werkzeug answers with the first matching rule, so an app route that repeats
301
+ # a core one (or another extension's) is never reached, with no error at all.
302
+ owners = {}
303
+ for rule in app.url_map.iter_rules():
304
+ # stk's own blueprints share /static on purpose: one folder, one answer.
305
+ if rule.endpoint == "static" or rule.endpoint.endswith(".static"):
306
+ continue
307
+ for method in rule.methods - {"HEAD", "OPTIONS"}:
308
+ owners.setdefault((method, rule.rule), []).append(rule.endpoint)
309
+ clashes = [
310
+ f"{method} {path}: {' and '.join(endpoints)}"
311
+ for (method, path), endpoints in sorted(owners.items())
312
+ if len(endpoints) > 1
313
+ ]
314
+ assert not clashes, (
315
+ "only the first endpoint ever answers; rename one route or drop the"
316
+ " extension that adds it:\n " + "\n ".join(clashes)
317
+ )
318
+
319
+
298
320
  @check("GET / answers 200, after any redirects")
299
321
  async def check_http_index(app):
300
322
  # An app may send / to its sign-in or home page; what it lands on must work.
@@ -15,8 +15,10 @@ VISIBLE_TEXT_JS = """() => {
15
15
  }"""
16
16
 
17
17
  # Visible texts of sibling elements that share a tag and classes: rows of a list.
18
+ # Only HTML elements: SVG shapes (a chart's paths, an icon's circles) repeat by
19
+ # design and have no innerText.
18
20
  ROWS_JS = """() => {
19
- const shown = el => el.getClientRects().length > 0;
21
+ const shown = el => el instanceof HTMLElement && el.getClientRects().length > 0;
20
22
  const sig = k => k.tagName + '.' + [...k.classList]
21
23
  .filter(c => !/active|selected|hover|focus|open|current/.test(c)).sort().join('.');
22
24
  const out = [];
@@ -303,9 +303,18 @@ async def _visit_smoke_page(context, base_url, name, path, screenshot_path=None)
303
303
  page.on("pageerror", _record_page_error)
304
304
  page.on("requestfailed", _record_failed_request)
305
305
  response = await page.goto(f"{base_url}{path}", wait_until="networkidle")
306
- low_contrast = await page.evaluate(CONTRAST_JS)
307
- text = await page.evaluate(VISIBLE_TEXT_JS)
308
- rows = await page.evaluate(ROWS_JS)
306
+ from playwright.async_api import Error as PlaywrightError
307
+
308
+ try:
309
+ low_contrast = await page.evaluate(CONTRAST_JS)
310
+ text = await page.evaluate(VISIBLE_TEXT_JS)
311
+ rows = await page.evaluate(ROWS_JS)
312
+ except PlaywrightError as exc:
313
+ # One page smoke cannot read fails that page, not the whole run.
314
+ console_entries.append(
315
+ {"type": "pageerror", "text": f"smoke could not read the page: {exc}"}
316
+ )
317
+ low_contrast, text, rows = [], "", []
309
318
  if screenshot_path is not None:
310
319
  screenshot_path.parent.mkdir(parents=True, exist_ok=True)
311
320
  await page.screenshot(path=str(screenshot_path), full_page=True)
@@ -1,4 +1,8 @@
1
- """Stored, per-user notifications. The row is the truth; the socket push is a nudge."""
1
+ """Stored, per-user notifications. The row is the truth; the socket push is a nudge.
2
+
3
+ Opt in with `extensions=[stk.notification, ...]` for the `/api/notifications`
4
+ routes and the layout's bell; `notify()` stores rows either way.
5
+ """
2
6
 
3
7
  import stk.extensions as ext
4
8
  from stk.notification.models import Notification
@@ -27,3 +31,11 @@ async def notify(user_id, title, link=None, kind=None, body=None):
27
31
  await db.commit()
28
32
  await broadcast({"type": "notification", **row.to_dict()}, user_id)
29
33
  return row
34
+
35
+
36
+ def init_app(app):
37
+ from stk.notification.views import bp_notification
38
+
39
+ app.extensions["stk.notification"] = True
40
+ app.jinja_env.globals["stk_notifications"] = True
41
+ app.register_blueprint(bp_notification)
@@ -1,8 +1,9 @@
1
+ import stk.notification
1
2
  from stk import Stk
2
3
 
3
4
  from __app__.settings import Config
4
5
 
5
6
  # Each extension is a package with init_app(app); order is precedence.
6
- extensions = []
7
+ extensions = [stk.notification]
7
8
 
8
9
  app = Stk(__name__, config=Config, extensions=extensions)
@@ -48,8 +48,10 @@
48
48
  <!-- Theme Switcher -->
49
49
  <theme-switcher></theme-switcher>
50
50
 
51
- <!-- Notifications -->
51
+ {% if stk_notifications %}
52
+ <!-- Notifications: only with stk.notification in the app's extensions -->
52
53
  <notification-dropdown ref="notifications"></notification-dropdown>
54
+ {% endif %}
53
55
 
54
56
  <!-- Connection Status -->
55
57
  <v-tooltip :text="wsConnected ? 'Real-time connected' : 'Reconnecting...'" location="bottom">
@@ -1,6 +1,7 @@
1
1
  import unittest
2
2
 
3
3
  from stk.cli.copy_rules import (
4
+ ROWS_JS,
4
5
  broken_value,
5
6
  copy_findings,
6
7
  hex_id,
@@ -110,3 +111,28 @@ class SmokeCopyReportTests(unittest.TestCase):
110
111
 
111
112
  if __name__ == "__main__":
112
113
  unittest.main()
114
+
115
+
116
+ class RowsInBrowserTest(unittest.IsolatedAsyncioTestCase):
117
+ """ROWS_JS runs in a real page: SVG shapes have no innerText."""
118
+
119
+ async def test_repeated_svg_shapes_are_skipped_and_html_rows_kept(self):
120
+ try:
121
+ from playwright.async_api import async_playwright
122
+
123
+ playwright = await async_playwright().start()
124
+ browser = await playwright.chromium.launch()
125
+ except Exception as exc: # no Playwright or no browser installed
126
+ self.skipTest(f"chromium unavailable: {exc}")
127
+ try:
128
+ page = await browser.new_page()
129
+ await page.set_content(
130
+ "<svg width='90' height='30'><circle cx='10' cy='10' r='5'/>"
131
+ "<circle cx='30' cy='10' r='5'/><circle cx='50' cy='10' r='5'/></svg>"
132
+ "<ul><li>Alpha</li><li>Beta</li><li>Gamma</li></ul>"
133
+ )
134
+ rows = await page.evaluate(ROWS_JS)
135
+ finally:
136
+ await browser.close()
137
+ await playwright.stop()
138
+ self.assertEqual(rows, [["Alpha", "Beta", "Gamma"]])
@@ -5,6 +5,7 @@ import unittest
5
5
  from quart_security import hash_password
6
6
 
7
7
  import stk.extensions as ext
8
+ import stk.notification
8
9
  from stk.app import Stk
9
10
  from stk.extensions import Base
10
11
  from stk.notification import notify
@@ -14,7 +15,7 @@ from stk.user.models import User
14
15
 
15
16
  class NotificationTests(unittest.IsolatedAsyncioTestCase):
16
17
  async def asyncSetUp(self):
17
- self.app = Stk(__name__, config=TestingConfig)
18
+ self.app = Stk(__name__, config=TestingConfig, extensions=[stk.notification])
18
19
  async with ext.engine.begin() as conn:
19
20
  await conn.run_sync(Base.metadata.create_all)
20
21
  async with ext.async_session_factory() as db:
@@ -0,0 +1,41 @@
1
+ """An app route that repeats a core one is never reached; verify must say so."""
2
+
3
+ import contextlib
4
+ import io
5
+ import unittest
6
+
7
+ from quart import Blueprint
8
+
9
+ import stk.notification
10
+ from stk import checks
11
+ from stk.app import Stk
12
+ from stk.checks import check_unique_routes
13
+ from stk.settings import TestingConfig
14
+
15
+
16
+ class RouteCollisionTest(unittest.IsolatedAsyncioTestCase):
17
+ async def test_core_routes_are_unique(self):
18
+ app = Stk(__name__, config=TestingConfig, extensions=[stk.notification])
19
+ with contextlib.redirect_stdout(io.StringIO()):
20
+ self.assertTrue(await check_unique_routes(app))
21
+
22
+ async def test_an_app_route_shadowed_by_a_core_route_fails(self):
23
+ app = Stk(__name__, config=TestingConfig, extensions=[stk.notification])
24
+ inbox = Blueprint("inbox", __name__)
25
+
26
+ @inbox.get("/api/notifications")
27
+ async def own_notifications():
28
+ return {"items": []}
29
+
30
+ app.register_blueprint(inbox)
31
+ with contextlib.redirect_stdout(io.StringIO()):
32
+ self.assertFalse(await check_unique_routes(app))
33
+ detail = checks.RESULTS[-1]["detail"]
34
+ self.assertIn("GET /api/notifications", detail)
35
+ self.assertIn("inbox.own_notifications", detail)
36
+
37
+ async def test_without_stk_notification_the_app_owns_the_path(self):
38
+ app = Stk(__name__, config=TestingConfig)
39
+ rules = {r.rule for r in app.url_map.iter_rules()}
40
+ self.assertNotIn("/api/notifications", rules)
41
+ self.assertNotIn("stk_notifications", app.jinja_env.globals)
@@ -321,7 +321,7 @@ class InitAndNewTests(unittest.TestCase):
321
321
  ast.parse((self.app / "acme" / "widget" / name).read_text())
322
322
  app_module = (self.app / "acme" / "app.py").read_text()
323
323
  self.assertIn("from acme import gadget, widget", app_module)
324
- self.assertIn("extensions = [widget, gadget]", app_module)
324
+ self.assertIn("extensions = [stk.notification, widget, gadget]", app_module)
325
325
 
326
326
  def test_new_starts_the_app_lineage_after_stk(self):
327
327
  (revision,) = (self.app / "acme" / "migrations").glob("*.py")
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes