stkit 17.0.2__tar.gz → 17.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {stkit-17.0.2 → stkit-17.1.1}/CHANGELOG.md +36 -0
- {stkit-17.0.2 → stkit-17.1.1}/PKG-INFO +12 -9
- {stkit-17.0.2 → stkit-17.1.1}/README.md +4 -4
- {stkit-17.0.2 → stkit-17.1.1}/pyproject.toml +10 -5
- {stkit-17.0.2 → stkit-17.1.1}/stk/__init__.py +1 -1
- {stkit-17.0.2 → stkit-17.1.1}/stk/app.py +32 -1
- {stkit-17.0.2 → stkit-17.1.1}/stk/cli/database.py +55 -23
- {stkit-17.0.2 → stkit-17.1.1}/stk/cli/doctor.py +1 -1
- {stkit-17.0.2 → stkit-17.1.1}/stk/migrations/__init__.py +22 -1
- {stkit-17.0.2 → stkit-17.1.1}/stk/migrations/env.py +3 -1
- {stkit-17.0.2 → stkit-17.1.1}/stk/scaffold/app/CLAUDE.md +2 -1
- {stkit-17.0.2 → stkit-17.1.1}/stk/scaffold/app/claude/skills/stk-api/SKILL.md +1 -1
- {stkit-17.0.2 → stkit-17.1.1}/stk/scaffold/app/claude/skills/stk-blueprint/SKILL.md +2 -2
- {stkit-17.0.2 → stkit-17.1.1}/stk/scaffold/app/claude/skills/stk-deploy/SKILL.md +1 -1
- {stkit-17.0.2 → stkit-17.1.1}/stk/scaffold/app/claude/skills/stk-lessons/SKILL.md +1 -1
- {stkit-17.0.2 → stkit-17.1.1}/stk/scaffold/app/claude/skills/stk-migrate/SKILL.md +1 -1
- stkit-17.1.1/stk/static/VERSIONS.txt +4 -0
- stkit-17.1.1/stk/static/css/vuetify.min.css +6 -0
- stkit-17.1.1/stk/static/icons/fonts/tabler-icons.animals.woff2 +0 -0
- stkit-17.1.1/stk/static/icons/fonts/tabler-icons.arrows-1.woff2 +0 -0
- stkit-17.1.1/stk/static/icons/fonts/tabler-icons.arrows-2.woff2 +0 -0
- stkit-17.1.1/stk/static/icons/fonts/tabler-icons.badges.woff2 +0 -0
- stkit-17.1.1/stk/static/icons/fonts/tabler-icons.brand-1.woff2 +0 -0
- stkit-17.1.1/stk/static/icons/fonts/tabler-icons.brand-2.woff2 +0 -0
- stkit-17.1.1/stk/static/icons/fonts/tabler-icons.buildings.woff2 +0 -0
- stkit-17.1.1/stk/static/icons/fonts/tabler-icons.charts.woff2 +0 -0
- stkit-17.1.1/stk/static/icons/fonts/tabler-icons.communication.woff2 +0 -0
- stkit-17.1.1/stk/static/icons/fonts/tabler-icons.computers.woff2 +0 -0
- stkit-17.1.1/stk/static/icons/fonts/tabler-icons.core.woff2 +0 -0
- stkit-17.1.1/stk/static/icons/fonts/tabler-icons.currencies.woff2 +0 -0
- stkit-17.1.1/stk/static/icons/fonts/tabler-icons.database.woff2 +0 -0
- stkit-17.1.1/stk/static/icons/fonts/tabler-icons.design-1.woff2 +0 -0
- stkit-17.1.1/stk/static/icons/fonts/tabler-icons.design-2.woff2 +0 -0
- stkit-17.1.1/stk/static/icons/fonts/tabler-icons.development.woff2 +0 -0
- stkit-17.1.1/stk/static/icons/fonts/tabler-icons.devices-1.woff2 +0 -0
- stkit-17.1.1/stk/static/icons/fonts/tabler-icons.devices-2.woff2 +0 -0
- stkit-17.1.1/stk/static/icons/fonts/tabler-icons.document.woff2 +0 -0
- stkit-17.1.1/stk/static/icons/fonts/tabler-icons.e-commerce.woff2 +0 -0
- stkit-17.1.1/stk/static/icons/fonts/tabler-icons.electrical.woff2 +0 -0
- stkit-17.1.1/stk/static/icons/fonts/tabler-icons.extensions.woff2 +0 -0
- stkit-17.1.1/stk/static/icons/fonts/tabler-icons.food.woff2 +0 -0
- stkit-17.1.1/stk/static/icons/fonts/tabler-icons.games.woff2 +0 -0
- stkit-17.1.1/stk/static/icons/fonts/tabler-icons.gender.woff2 +0 -0
- stkit-17.1.1/stk/static/icons/fonts/tabler-icons.gestures.woff2 +0 -0
- stkit-17.1.1/stk/static/icons/fonts/tabler-icons.health.woff2 +0 -0
- stkit-17.1.1/stk/static/icons/fonts/tabler-icons.laundry.woff2 +0 -0
- stkit-17.1.1/stk/static/icons/fonts/tabler-icons.letters.woff2 +0 -0
- stkit-17.1.1/stk/static/icons/fonts/tabler-icons.logic.woff2 +0 -0
- stkit-17.1.1/stk/static/icons/fonts/tabler-icons.map.woff2 +0 -0
- stkit-17.1.1/stk/static/icons/fonts/tabler-icons.math.woff2 +0 -0
- stkit-17.1.1/stk/static/icons/fonts/tabler-icons.media.woff2 +0 -0
- stkit-17.1.1/stk/static/icons/fonts/tabler-icons.mood.woff2 +0 -0
- stkit-17.1.1/stk/static/icons/fonts/tabler-icons.nature.woff2 +0 -0
- stkit-17.1.1/stk/static/icons/fonts/tabler-icons.numbers.woff2 +0 -0
- stkit-17.1.1/stk/static/icons/fonts/tabler-icons.photography.woff2 +0 -0
- stkit-17.1.1/stk/static/icons/fonts/tabler-icons.shapes.woff2 +0 -0
- stkit-17.1.1/stk/static/icons/fonts/tabler-icons.sport.woff2 +0 -0
- stkit-17.1.1/stk/static/icons/fonts/tabler-icons.symbols.woff2 +0 -0
- stkit-17.1.1/stk/static/icons/fonts/tabler-icons.system-1.woff2 +0 -0
- stkit-17.1.1/stk/static/icons/fonts/tabler-icons.system-2.woff2 +0 -0
- stkit-17.1.1/stk/static/icons/fonts/tabler-icons.system-3.woff2 +0 -0
- stkit-17.1.1/stk/static/icons/fonts/tabler-icons.text.woff2 +0 -0
- stkit-17.1.1/stk/static/icons/fonts/tabler-icons.vehicles.woff2 +0 -0
- stkit-17.1.1/stk/static/icons/fonts/tabler-icons.version-control.woff2 +0 -0
- stkit-17.1.1/stk/static/icons/fonts/tabler-icons.weather.woff2 +0 -0
- stkit-17.1.1/stk/static/icons/fonts/tabler-icons.zodiac.woff2 +0 -0
- stkit-17.1.1/stk/static/icons/tabler-icons.min.css +4 -0
- stkit-17.1.1/stk/static/js/axios.min.js +5 -0
- stkit-17.1.1/stk/static/js/vue.min.js +14 -0
- stkit-17.1.1/stk/static/js/vuetify.min.js +3886 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/templates/dashboard.html +3 -3
- {stkit-17.0.2 → stkit-17.1.1}/stk/templates/index.html +1 -1
- {stkit-17.0.2 → stkit-17.1.1}/stk/templates/layout.html +2 -1
- {stkit-17.0.2 → stkit-17.1.1}/tests/test_composition.py +26 -0
- {stkit-17.0.2 → stkit-17.1.1}/tests/test_migration_drift.py +30 -0
- {stkit-17.0.2 → stkit-17.1.1}/tests/test_production_security.py +30 -5
- stkit-17.1.1/tests/test_revision_imports.py +108 -0
- {stkit-17.0.2 → stkit-17.1.1}/tests/test_scaffolder.py +2 -1
- stkit-17.0.2/stk/static/VERSIONS.txt +0 -4
- stkit-17.0.2/stk/static/css/vuetify.min.css +0 -6
- stkit-17.0.2/stk/static/icons/fonts/tabler-icons.woff2 +0 -0
- stkit-17.0.2/stk/static/icons/tabler-icons.min.css +0 -4
- stkit-17.0.2/stk/static/js/axios.min.js +0 -5
- stkit-17.0.2/stk/static/js/vue.min.js +0 -13
- stkit-17.0.2/stk/static/js/vuetify.min.js +0 -3148
- {stkit-17.0.2 → stkit-17.1.1}/.gitignore +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/LICENSE +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/SECURITY.md +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/examples/minimal/.env-sample +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/examples/minimal/.gitignore +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/examples/minimal/README.md +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/examples/minimal/minimal/__init__.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/examples/minimal/minimal/app.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/examples/minimal/minimal/migrations/364232ae77d5_add_note.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/examples/minimal/minimal/note/__init__.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/examples/minimal/minimal/note/models.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/examples/minimal/minimal/note/templates/note/index.html +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/examples/minimal/minimal/note/views.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/examples/minimal/minimal/settings.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/examples/minimal/minimal/templates/layout.html +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/examples/minimal/pyproject.toml +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/examples/minimal/tests/__init__.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/examples/minimal/tests/test_app.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/examples/minimal/uv.lock +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/__main__.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/agent_login.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/checks.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/cli/__init__.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/cli/agent.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/cli/base.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/cli/develop.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/cli/main.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/cli/reports.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/cli/smoke.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/cli/users.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/cli/watch.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/extensions.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/migrations/script.py.mako +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/migrations/versions/stk_0001_initial_schema.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/portal/views.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/public/__init__.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/public/models.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/public/views.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/scaffold/__init__.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/scaffold/app/Dockerfile +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/scaffold/app/README.md +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/scaffold/app/__app__/__init__.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/scaffold/app/__app__/app.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/scaffold/app/__app__/settings.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/scaffold/app/docker-compose.yml +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/scaffold/app/dockerignore +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/scaffold/app/env-sample +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/scaffold/app/github/workflows/checks.yml +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/scaffold/app/gitignore +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/scaffold/app/nginx/nginx.conf +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/scaffold/app/nginx/stk.conf +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/scaffold/app/postgres/init-db.sh +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/scaffold/app/pyproject.toml +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/scaffold/app/tests/__init__.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/scaffold/app/tests/test_app.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/scaffold/generator.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/scaffold/templates.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/security.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/settings.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/shell.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/static/css/app.css +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/static/css/layout.css +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/static/img/auth-bg.webp +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/static/img/favicon.ico +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/static/img/stk.svg +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/static/js/components/NotificationDropdown.js +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/static/js/components/ThemeSwitcher.js +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/static/js/components/TransitionExpand.js +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/static/js/components/VerticalNavGroup.js +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/static/js/components/VerticalNavLink.js +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/static/js/components/VerticalNavSectionTitle.js +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/static/js/components/index.js +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/static/js/config.js +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/static/robots.txt +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/tasks/__init__.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/templates/401.html +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/templates/404.html +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/templates/500.html +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/templates/_brand.html +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/templates/auth_layout.html +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/templates/cms/activities.html +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/templates/cms/roles.html +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/templates/cms/users.html +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/templates/login-layout.html +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/templates/security/_macros.html +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/templates/security/_messages.html +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/templates/security/change_password.html +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/templates/security/confirm_email.html +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/templates/security/email/change_notice.html +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/templates/security/email/change_notice.txt +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/templates/security/email/confirmation_instructions.html +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/templates/security/email/confirmation_instructions.txt +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/templates/security/email/login_instructions.html +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/templates/security/email/login_instructions.txt +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/templates/security/email/reset_instructions.html +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/templates/security/email/reset_instructions.txt +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/templates/security/email/reset_notice.html +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/templates/security/email/reset_notice.txt +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/templates/security/email/welcome.html +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/templates/security/email/welcome.txt +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/templates/security/forgot_password.html +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/templates/security/login_user.html +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/templates/security/mf_recovery.html +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/templates/security/mf_recovery_codes.html +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/templates/security/register_user.html +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/templates/security/reset_password.html +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/templates/security/send_confirmation.html +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/templates/security/send_login.html +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/templates/security/two_factor_select.html +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/templates/security/two_factor_setup.html +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/templates/security/two_factor_verify_code.html +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/templates/security/wan_register.html +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/templates/security/wan_signin.html +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/templates/security/wan_verify.html +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/user/__init__.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/user/forms.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/user/models.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/user/views.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/utils/email.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/utils/ratelimit.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/stk/websocket.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/tests/test_agent_operability.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/tests/test_app_index.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/tests/test_branding.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/tests/test_builder_ux.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/tests/test_cli.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/tests/test_csrf_exempt.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/tests/test_error_handler.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/tests/test_security_compatibility.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/tests/test_shell.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/tests/test_smoke_pages.py +0 -0
- {stkit-17.0.2 → stkit-17.1.1}/tests/test_static_versioning.py +0 -0
|
@@ -1,5 +1,41 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v17.1.1 (2026-10-28)
|
|
4
|
+
|
|
5
|
+
No migration required.
|
|
6
|
+
|
|
7
|
+
- The docs move to https://docs.stkit.dev, matching the PyPI name. An app made by an earlier `stk init` points its `CLAUDE.md` and `.claude/skills/` at docs.stk.dev: replace it with docs.stkit.dev.
|
|
8
|
+
- stkit's PyPI page links to the docs, source and changelog.
|
|
9
|
+
- Vendored Vue 3.5.44, Vuetify 4.3.1, axios 1.21.0 (fixes GHSA-m8m8-qj5v-23w3, GHSA-x97p-jq2g-jp4f and ten other axios advisories) and Tabler icons 3.50.0. Tabler now ships its font as unicode-range subsets in `icons/fonts/`, so the browser loads only the groups a page uses; `icons/fonts/tabler-icons.woff2` is gone. An app that preloads that file links `icons/fonts/tabler-icons.core.woff2` instead (Frontend).
|
|
10
|
+
- stkit's dependency minimums no longer allow versions with known vulnerabilities: cryptography 50.0.0, aiosmtplib 5.1.2, orjson 3.11.6, python-dotenv 1.2.2. An app that pins an older one gets a resolver conflict; raise its pin.
|
|
11
|
+
- CI audits the locked dependencies for known vulnerabilities with `uv audit`.
|
|
12
|
+
|
|
13
|
+
## v17.1.0 (2026-10-25)
|
|
14
|
+
|
|
15
|
+
No migration required.
|
|
16
|
+
|
|
17
|
+
- `app_navigation()` returns the app's `app.navigation` entries alone, without stk's Dashboard, Account and Administration sections, for a layout that draws its own sidebar (Frontend, Navigation).
|
|
18
|
+
- The layout ships an import map, `{{ import_map() }}`, that maps every `.js` and `.mjs` file in the app's, extensions' and stk's static folders to its versioned URL, app first. A module imported by another module is fresh after a deploy too. A layout that does not extend `stk/layout.html` calls it in its `<head>` (Frontend).
|
|
19
|
+
- `build_alembic_config("myapp", url)` takes the app's import name with a URL, so a test can migrate a throwaway database before it builds its app. Passing the app still works (Models, Migrations).
|
|
20
|
+
- `stk db revision` imports the module of each column type Alembic writes by its path (pgvector's `HALFVEC` and `BIT`, an app's `UserDefinedType`), then sorts the revision's imports and formats it with the app's ruff config. Before, such revisions failed with a `NameError`.
|
|
21
|
+
- `stk db revision` warns about each column-level `CheckConstraint` in the columns it adds: Alembic autogenerate leaves them out. Declare checks in `__table_args__`, or add them to the revision by hand (Models, Migrations).
|
|
22
|
+
- Configuration documents app paths in `settings.py` (`Path(__file__).resolve().parent.parent`) in place of the removed `Config.PROJECT_ROOT`, and `app.root_path` and `app.instance_path` at runtime.
|
|
23
|
+
|
|
24
|
+
### Upgrade considerations
|
|
25
|
+
|
|
26
|
+
Nothing breaks. Apps can delete what they wrote around these gaps:
|
|
27
|
+
|
|
28
|
+
- A Jinja global that returns `app.navigation` (Sealbook's `rail_navigation` in `sealbook/shell/__init__.py`): call `app_navigation()` in the layout instead.
|
|
29
|
+
- An app import map (Sealbook's `import_map()` and `MODULE_DIRS` in `sealbook/assets.py`): stk's `import_map()` global has the same name, so a copied layout that calls `{{ import_map() }}` keeps working. stk's map also lists minified `.js` files; they are harmless there.
|
|
30
|
+
- Tests that import the module-level app only to call `build_alembic_config(app, url)`: pass `"myapp"` instead.
|
|
31
|
+
- Nothing in existing revisions: hand-written imports there stay correct. New revisions import their types themselves.
|
|
32
|
+
|
|
33
|
+
Paste-ready prompt for a coding agent:
|
|
34
|
+
|
|
35
|
+
```text
|
|
36
|
+
Upgrade this app to stkit 17.1.0 (bump the pin, `uv lock`). Then delete the workarounds 17.1.0 makes unnecessary: (1) any Jinja global returning app.navigation; call app_navigation() in the layout instead. (2) any app-built import map of versioned static modules; stk's import_map() global replaces it, so keep `{{ import_map() }}` in a copied layout and delete the app's function and its registration, plus test assertions that minified files are absent from the map. (3) in tests, `build_alembic_config(app, url, ...)` where the app was imported only for that: pass the app's import name, `build_alembic_config("<package>", url, ...)`, and drop the import. Leave existing migration revisions alone, including their hand-written imports and checks. Done when the tests, `uv run stk verify` and `uv run stk smoke` pass.
|
|
37
|
+
```
|
|
38
|
+
|
|
3
39
|
## v17.0.2 (2026-10-25)
|
|
4
40
|
|
|
5
41
|
No migration required.
|
|
@@ -1,28 +1,31 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: stkit
|
|
3
|
-
Version: 17.
|
|
3
|
+
Version: 17.1.1
|
|
4
4
|
Summary: An async Quart framework optimized for AI-assisted development workflows
|
|
5
|
+
Project-URL: Documentation, https://docs.stkit.dev
|
|
6
|
+
Project-URL: Source, https://github.com/level09/stk
|
|
7
|
+
Project-URL: Changelog, https://github.com/level09/stk/blob/master/CHANGELOG.md
|
|
5
8
|
Author-email: Level09 <hello@level09.com>
|
|
6
9
|
License: MIT
|
|
7
10
|
License-File: LICENSE
|
|
8
11
|
Requires-Python: >=3.11
|
|
9
|
-
Requires-Dist: aiosmtplib>=5.
|
|
12
|
+
Requires-Dist: aiosmtplib>=5.1.2
|
|
10
13
|
Requires-Dist: aiosqlite>=0.20.0
|
|
11
14
|
Requires-Dist: alembic>=1.16.0
|
|
12
15
|
Requires-Dist: authlib<2,>=1.9.1
|
|
13
16
|
Requires-Dist: bcrypt>=4.3.0
|
|
14
17
|
Requires-Dist: blinker>=1.9.0
|
|
15
18
|
Requires-Dist: click>=8.3.1
|
|
16
|
-
Requires-Dist: cryptography>=
|
|
19
|
+
Requires-Dist: cryptography>=50.0.0
|
|
17
20
|
Requires-Dist: email-validator>=2.2.0
|
|
18
21
|
Requires-Dist: httpx2>=2.16.0
|
|
19
22
|
Requires-Dist: jinja2>=3.1.6
|
|
20
23
|
Requires-Dist: joserfc<2,>=1.7.1
|
|
21
24
|
Requires-Dist: libpass>=1.9.0
|
|
22
|
-
Requires-Dist: orjson>=3.
|
|
25
|
+
Requires-Dist: orjson>=3.11.6
|
|
23
26
|
Requires-Dist: pillow>=13.0.0
|
|
24
27
|
Requires-Dist: psycopg2-binary>=2.9.10
|
|
25
|
-
Requires-Dist: python-dotenv>=1.
|
|
28
|
+
Requires-Dist: python-dotenv>=1.2.2
|
|
26
29
|
Requires-Dist: qrcode>=8.2
|
|
27
30
|
Requires-Dist: quart-security<3,>=2.0.3
|
|
28
31
|
Requires-Dist: quart-session>=3.0.0
|
|
@@ -66,8 +69,8 @@ Description-Content-Type: text/markdown
|
|
|
66
69
|
</p>
|
|
67
70
|
|
|
68
71
|
<p align="center">
|
|
69
|
-
<a href="https://docs.
|
|
70
|
-
<a href="https://docs.
|
|
72
|
+
<a href="https://docs.stkit.dev">Docs</a> ·
|
|
73
|
+
<a href="https://docs.stkit.dev/llms.txt">llms.txt</a> ·
|
|
71
74
|
<a href="https://github.com/level09/stk/wiki">Tutorials</a>
|
|
72
75
|
</p>
|
|
73
76
|
|
|
@@ -126,8 +129,8 @@ stk ships what actually matters:
|
|
|
126
129
|
|
|
127
130
|
## Documentation
|
|
128
131
|
|
|
129
|
-
Everything else lives at [docs.
|
|
130
|
-
[quick start](https://docs.
|
|
132
|
+
Everything else lives at [docs.stkit.dev](https://docs.stkit.dev), and in this repo at `docs/src/content/docs/`:
|
|
133
|
+
[quick start](https://docs.stkit.dev/getting-started/), [build loop](https://docs.stkit.dev/build-loop/), [models](https://docs.stkit.dev/models/), [frontend](https://docs.stkit.dev/frontend/), [authentication](https://docs.stkit.dev/authentication/), [configuration](https://docs.stkit.dev/configuration/), [CLI reference](https://docs.stkit.dev/cli/), [deployment](https://docs.stkit.dev/deployment/), and [working with agents](https://docs.stkit.dev/ai-coding/). Upgrade notes are in `CHANGELOG.md`; the production security contract is `SECURITY.md`.
|
|
131
134
|
|
|
132
135
|
## License
|
|
133
136
|
|
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
</p>
|
|
21
21
|
|
|
22
22
|
<p align="center">
|
|
23
|
-
<a href="https://docs.
|
|
24
|
-
<a href="https://docs.
|
|
23
|
+
<a href="https://docs.stkit.dev">Docs</a> ·
|
|
24
|
+
<a href="https://docs.stkit.dev/llms.txt">llms.txt</a> ·
|
|
25
25
|
<a href="https://github.com/level09/stk/wiki">Tutorials</a>
|
|
26
26
|
</p>
|
|
27
27
|
|
|
@@ -80,8 +80,8 @@ stk ships what actually matters:
|
|
|
80
80
|
|
|
81
81
|
## Documentation
|
|
82
82
|
|
|
83
|
-
Everything else lives at [docs.
|
|
84
|
-
[quick start](https://docs.
|
|
83
|
+
Everything else lives at [docs.stkit.dev](https://docs.stkit.dev), and in this repo at `docs/src/content/docs/`:
|
|
84
|
+
[quick start](https://docs.stkit.dev/getting-started/), [build loop](https://docs.stkit.dev/build-loop/), [models](https://docs.stkit.dev/models/), [frontend](https://docs.stkit.dev/frontend/), [authentication](https://docs.stkit.dev/authentication/), [configuration](https://docs.stkit.dev/configuration/), [CLI reference](https://docs.stkit.dev/cli/), [deployment](https://docs.stkit.dev/deployment/), and [working with agents](https://docs.stkit.dev/ai-coding/). Upgrade notes are in `CHANGELOG.md`; the production security contract is `SECURITY.md`.
|
|
85
85
|
|
|
86
86
|
## License
|
|
87
87
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "stkit"
|
|
3
|
-
version = "17.
|
|
3
|
+
version = "17.1.1"
|
|
4
4
|
description = "An async Quart framework optimized for AI-assisted development workflows"
|
|
5
5
|
authors = [
|
|
6
6
|
{name = "Level09", email = "hello@level09.com"}
|
|
@@ -11,22 +11,22 @@ requires-python = ">=3.11"
|
|
|
11
11
|
|
|
12
12
|
dependencies = [
|
|
13
13
|
"alembic>=1.16.0",
|
|
14
|
-
"aiosmtplib>=5.
|
|
14
|
+
"aiosmtplib>=5.1.2",
|
|
15
15
|
"aiosqlite>=0.20.0",
|
|
16
16
|
"authlib>=1.9.1,<2",
|
|
17
17
|
"bcrypt>=4.3.0",
|
|
18
18
|
"blinker>=1.9.0",
|
|
19
19
|
"click>=8.3.1",
|
|
20
|
-
"cryptography>=
|
|
20
|
+
"cryptography>=50.0.0",
|
|
21
21
|
"email-validator>=2.2.0",
|
|
22
22
|
"httpx2>=2.16.0",
|
|
23
23
|
"jinja2>=3.1.6",
|
|
24
24
|
"joserfc>=1.7.1,<2",
|
|
25
25
|
"libpass>=1.9.0",
|
|
26
|
-
"orjson>=3.
|
|
26
|
+
"orjson>=3.11.6",
|
|
27
27
|
"pillow>=13.0.0",
|
|
28
28
|
"psycopg2-binary>=2.9.10",
|
|
29
|
-
"python-dotenv>=1.
|
|
29
|
+
"python-dotenv>=1.2.2",
|
|
30
30
|
"qrcode>=8.2",
|
|
31
31
|
"quart>=0.20.0",
|
|
32
32
|
"quart-security>=2.0.3,<3",
|
|
@@ -39,6 +39,11 @@ dependencies = [
|
|
|
39
39
|
]
|
|
40
40
|
|
|
41
41
|
|
|
42
|
+
[project.urls]
|
|
43
|
+
Documentation = "https://docs.stkit.dev"
|
|
44
|
+
Source = "https://github.com/level09/stk"
|
|
45
|
+
Changelog = "https://github.com/level09/stk/blob/master/CHANGELOG.md"
|
|
46
|
+
|
|
42
47
|
[project.scripts]
|
|
43
48
|
stk = "stk.cli.main:main"
|
|
44
49
|
|
|
@@ -4,11 +4,20 @@ import hashlib
|
|
|
4
4
|
import importlib
|
|
5
5
|
import importlib.util
|
|
6
6
|
import inspect
|
|
7
|
+
import json
|
|
7
8
|
import os
|
|
8
9
|
from pathlib import Path
|
|
9
10
|
|
|
10
11
|
from jinja2 import ChoiceLoader, FileSystemLoader, PrefixLoader
|
|
11
|
-
from
|
|
12
|
+
from markupsafe import Markup
|
|
13
|
+
from quart import (
|
|
14
|
+
Quart,
|
|
15
|
+
g,
|
|
16
|
+
render_template,
|
|
17
|
+
request,
|
|
18
|
+
send_from_directory,
|
|
19
|
+
url_for,
|
|
20
|
+
)
|
|
12
21
|
from quart_security import Security, SQLAlchemyUserDatastore
|
|
13
22
|
from quart_security.views import _ensure_csrf_token
|
|
14
23
|
from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine
|
|
@@ -232,6 +241,26 @@ def register_static_versioning(app):
|
|
|
232
241
|
)
|
|
233
242
|
return response
|
|
234
243
|
|
|
244
|
+
def import_map():
|
|
245
|
+
"""Map each module's plain URL to its versioned one, so a module imported by
|
|
246
|
+
another module (`import "./x.js"`) is fresh after a deploy too."""
|
|
247
|
+
names = sorted(
|
|
248
|
+
{
|
|
249
|
+
path.relative_to(folder).as_posix()
|
|
250
|
+
for folder in app.static_folders
|
|
251
|
+
for path in folder.rglob("*")
|
|
252
|
+
if path.suffix in (".js", ".mjs")
|
|
253
|
+
}
|
|
254
|
+
)
|
|
255
|
+
imports = {
|
|
256
|
+
f"{app.static_url_path}/{name}": url_for("static", filename=name)
|
|
257
|
+
for name in names
|
|
258
|
+
}
|
|
259
|
+
body = json.dumps({"imports": imports}).replace("</", "<\\/")
|
|
260
|
+
return Markup(f'<script type="importmap">{body}</script>')
|
|
261
|
+
|
|
262
|
+
app.jinja_env.globals["import_map"] = import_map
|
|
263
|
+
|
|
235
264
|
|
|
236
265
|
CORE_NAVIGATION_START = [
|
|
237
266
|
{"heading": "Main"},
|
|
@@ -267,6 +296,8 @@ def register_navigation(app):
|
|
|
267
296
|
*app.navigation,
|
|
268
297
|
*CORE_NAVIGATION_END,
|
|
269
298
|
]
|
|
299
|
+
# The app's entries alone, for a layout that draws its own sidebar.
|
|
300
|
+
app.jinja_env.globals["app_navigation"] = lambda: app.navigation
|
|
270
301
|
|
|
271
302
|
|
|
272
303
|
def register_errorhandlers(app):
|
|
@@ -79,6 +79,44 @@ def build_migration_drift_report(app=None, database_url=None):
|
|
|
79
79
|
return [_describe_diff(diff) for diff in _flatten(diffs)]
|
|
80
80
|
|
|
81
81
|
|
|
82
|
+
def _column_checks(op):
|
|
83
|
+
"""`table.column` for each column an autogenerated op adds with its own check."""
|
|
84
|
+
from sqlalchemy import CheckConstraint, Column
|
|
85
|
+
|
|
86
|
+
for child in getattr(op, "ops", ()):
|
|
87
|
+
yield from _column_checks(child)
|
|
88
|
+
columns = [*getattr(op, "columns", ()), getattr(op, "column", None)]
|
|
89
|
+
for column in columns:
|
|
90
|
+
if isinstance(column, Column) and any(
|
|
91
|
+
isinstance(c, CheckConstraint) for c in column.constraints
|
|
92
|
+
):
|
|
93
|
+
yield f"{column.table.name}.{column.name}"
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _warn_column_checks(context, revision, directives):
|
|
97
|
+
# Alembic renders table-level checks only; a column's own check is left out.
|
|
98
|
+
for name in _column_checks(directives[0].upgrade_ops):
|
|
99
|
+
console.print(
|
|
100
|
+
f"[yellow]{name}: autogenerate leaves out a column-level CheckConstraint."
|
|
101
|
+
" Move it to __table_args__, or add it to this revision by hand.[/]"
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _tidy_revision(path):
|
|
106
|
+
"""Sort the revision's imports and format it with the app's own ruff config."""
|
|
107
|
+
import importlib.util
|
|
108
|
+
import subprocess
|
|
109
|
+
import sys
|
|
110
|
+
|
|
111
|
+
if importlib.util.find_spec("ruff") is None:
|
|
112
|
+
console.print("[yellow]ruff is not installed: revision imports are unsorted[/]")
|
|
113
|
+
return
|
|
114
|
+
for args in (["check", "--fix", "--select", "I"], ["format"]):
|
|
115
|
+
subprocess.run(
|
|
116
|
+
[sys.executable, "-m", "ruff", *args, "--quiet", path], check=True
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
|
|
82
120
|
def generate_revision(app, message, *, autogenerate=True, head=None, quiet=False):
|
|
83
121
|
"""Write a revision in the app's lineage, or in `head`'s when given.
|
|
84
122
|
|
|
@@ -89,35 +127,29 @@ def generate_revision(app, message, *, autogenerate=True, head=None, quiet=False
|
|
|
89
127
|
|
|
90
128
|
config = build_alembic_config(app, quiet=quiet)
|
|
91
129
|
if head is not None:
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
else {
|
|
107
|
-
"head": "base",
|
|
108
|
-
"branch_label": label,
|
|
109
|
-
"version_path": str(app_versions(app)),
|
|
110
|
-
}
|
|
111
|
-
)
|
|
112
|
-
app_versions(app).mkdir(exist_ok=True)
|
|
113
|
-
return run_alembic(
|
|
130
|
+
options = {"head": head}
|
|
131
|
+
else:
|
|
132
|
+
script = ScriptDirectory.from_config(config)
|
|
133
|
+
label = app_label(app)
|
|
134
|
+
started = any(label in rev.branch_labels for rev in script.walk_revisions())
|
|
135
|
+
options = {"depends_on": script.get_revision(f"{CORE_LABEL}@head").revision}
|
|
136
|
+
if started:
|
|
137
|
+
options["head"] = f"{label}@head"
|
|
138
|
+
else:
|
|
139
|
+
options.update(
|
|
140
|
+
head="base", branch_label=label, version_path=str(app_versions(app))
|
|
141
|
+
)
|
|
142
|
+
app_versions(app).mkdir(exist_ok=True)
|
|
143
|
+
revision = run_alembic(
|
|
114
144
|
command.revision,
|
|
115
145
|
config,
|
|
116
146
|
message=message,
|
|
117
147
|
autogenerate=autogenerate,
|
|
118
|
-
|
|
148
|
+
process_revision_directives=_warn_column_checks,
|
|
119
149
|
**options,
|
|
120
150
|
)
|
|
151
|
+
_tidy_revision(revision.path)
|
|
152
|
+
return revision
|
|
121
153
|
|
|
122
154
|
|
|
123
155
|
@click.command()
|
|
@@ -168,7 +168,7 @@ def _check_assets():
|
|
|
168
168
|
"js/vuetify.min.js",
|
|
169
169
|
"css/vuetify.min.css",
|
|
170
170
|
"icons/tabler-icons.min.css",
|
|
171
|
-
"icons/fonts/tabler-icons.woff2",
|
|
171
|
+
"icons/fonts/tabler-icons.core.woff2",
|
|
172
172
|
)
|
|
173
173
|
]
|
|
174
174
|
missing = [path.name for path in expected if not path.exists()]
|
|
@@ -7,6 +7,7 @@ label; an app's carry its package name and live in `<package>/migrations/`.
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
9
|
import importlib
|
|
10
|
+
import importlib.util
|
|
10
11
|
import os
|
|
11
12
|
import pkgutil
|
|
12
13
|
import tempfile
|
|
@@ -55,6 +56,17 @@ def include_object(obj, name, type_, reflected, compare_to):
|
|
|
55
56
|
return not (type_ == "table" and name in EXTENSION_TABLES)
|
|
56
57
|
|
|
57
58
|
|
|
59
|
+
def render_item(type_, obj, autogen_context):
|
|
60
|
+
"""Import the module of a type Alembic renders by its path (pgvector's, the app's).
|
|
61
|
+
|
|
62
|
+
Alembic writes such a type as `pgvector.sqlalchemy.halfvec.HALFVEC(dim=3)` and
|
|
63
|
+
imports only SQLAlchemy dialects, so the revision would not run.
|
|
64
|
+
"""
|
|
65
|
+
if type_ == "type" and not type(obj).__module__.startswith("sqlalchemy."):
|
|
66
|
+
autogen_context.imports.add(f"import {type(obj).__module__}")
|
|
67
|
+
return False
|
|
68
|
+
|
|
69
|
+
|
|
58
70
|
def sync_url(database_url: str) -> str:
|
|
59
71
|
"""Convert an async driver URL to the sync driver Alembic uses."""
|
|
60
72
|
url = make_url(database_url)
|
|
@@ -71,11 +83,20 @@ def app_label(app) -> str:
|
|
|
71
83
|
|
|
72
84
|
|
|
73
85
|
def app_versions(app) -> Path:
|
|
86
|
+
"""The app's migrations directory; `app` is the app or its import name."""
|
|
87
|
+
if isinstance(app, str):
|
|
88
|
+
return Path(importlib.util.find_spec(app).origin).parent / "migrations"
|
|
74
89
|
return Path(app.root_path) / "migrations"
|
|
75
90
|
|
|
76
91
|
|
|
77
92
|
def build_alembic_config(app=None, url=None, *, quiet=False) -> AlembicConfig:
|
|
78
|
-
"""Build an Alembic config for stk's lineage plus the app's, if given.
|
|
93
|
+
"""Build an Alembic config for stk's lineage plus the app's, if given.
|
|
94
|
+
|
|
95
|
+
`app` is the app, or its import name (`"myapp"`) with an explicit `url`, so a
|
|
96
|
+
test can migrate a throwaway database before it builds the app.
|
|
97
|
+
"""
|
|
98
|
+
if isinstance(app, str) and url is None:
|
|
99
|
+
raise ValueError("pass url when app is an import name")
|
|
79
100
|
config = AlembicConfig()
|
|
80
101
|
config.set_main_option("script_location", str(SCRIPT_DIR))
|
|
81
102
|
config.set_main_option("path_separator", "os")
|
|
@@ -5,7 +5,7 @@ import logging
|
|
|
5
5
|
from alembic import context
|
|
6
6
|
from sqlalchemy import engine_from_config, pool
|
|
7
7
|
|
|
8
|
-
from stk.migrations import get_target_metadata, include_object
|
|
8
|
+
from stk.migrations import get_target_metadata, include_object, render_item
|
|
9
9
|
|
|
10
10
|
config = context.config
|
|
11
11
|
|
|
@@ -25,6 +25,7 @@ def run_migrations_offline() -> None:
|
|
|
25
25
|
compare_type=True,
|
|
26
26
|
compare_server_default=True,
|
|
27
27
|
include_object=include_object,
|
|
28
|
+
render_item=render_item,
|
|
28
29
|
render_as_batch=url.startswith("sqlite"),
|
|
29
30
|
)
|
|
30
31
|
|
|
@@ -44,6 +45,7 @@ def run_migrations_online() -> None:
|
|
|
44
45
|
compare_type=True,
|
|
45
46
|
compare_server_default=True,
|
|
46
47
|
include_object=include_object,
|
|
48
|
+
render_item=render_item,
|
|
47
49
|
# SQLite alters tables by copying them; batch mode does that for us.
|
|
48
50
|
render_as_batch=connection.dialect.name == "sqlite",
|
|
49
51
|
)
|
|
@@ -11,7 +11,7 @@ same path in `__app__/templates/` or `__app__/static/`; `stk/<name>` is the orig
|
|
|
11
11
|
## Where truth lives
|
|
12
12
|
|
|
13
13
|
1. **The running app.** Ask it before reading files: `uv run stk inspect context --json` (routes with auth, models), `uv run stk inspect commands --json` (every command and option), `uv run stk doctor` (app state, each problem with its fix). `uv run stk new <module>` writes the canonical CRUD extension; copy its patterns.
|
|
14
|
-
2. **stk's docs** at https://docs.
|
|
14
|
+
2. **stk's docs** at https://docs.stkit.dev, indexed for agents at https://docs.stkit.dev/llms.txt. Read the page for your task before writing code.
|
|
15
15
|
3. **Skills** in `.claude/skills/` (mirrored at `.agents/skills/`): `stk-blueprint`, `stk-api`, `stk-migrate`, `stk-deploy`, and `stk-lessons` (when a gotcha the docs missed costs you time, log one line in `.stk/lessons.md`).
|
|
16
16
|
|
|
17
17
|
## Commands
|
|
@@ -48,6 +48,7 @@ Backend:
|
|
|
48
48
|
|
|
49
49
|
Frontend:
|
|
50
50
|
- Vue Options API (`data()`, `methods`, `mounted()`), never Composition API. Delimiters are `${}`. Every page uses `mixins: [layoutMixin]` and calls `registerStkComponents(app)` before mount.
|
|
51
|
+
- Jinja output inside `#app` is compiled by Vue: user text there runs as code. Pass it as data, or put `v-pre` on its element.
|
|
51
52
|
- Server data JSON `<script>` tags go in `{% block js %}`, never `{% block content %}`: Vue strips scripts inside `#app`.
|
|
52
53
|
- Template expressions cannot see page globals: `app.config.globalProperties.name = value` before mount. The production build fails silently, so look at the rendered page.
|
|
53
54
|
- Vuetify 4 uses MD3 type classes (`text-title-large`, not `text-h6`); MD2 classes do nothing. Icons are Tabler (`ti ti-*`), never MDI.
|
|
@@ -26,7 +26,7 @@ Existing API routes:
|
|
|
26
26
|
- `POST /api/<singular>/<id>` — Update (POST, not PUT — stk convention)
|
|
27
27
|
- `DELETE /api/<singular>/<id>` — Delete
|
|
28
28
|
|
|
29
|
-
3. **Follow the conventions** in https://docs.
|
|
29
|
+
3. **Follow the conventions** in https://docs.stkit.dev/api/: blueprint-level auth, `{item: {...}}`
|
|
30
30
|
payloads, `{items, total, perPage}` list responses via `orjson`, `Activity.register` on
|
|
31
31
|
every mutation, rollback plus `log.exception` on failure, and `{"message": ...}` with
|
|
32
32
|
400/404/412. The reference implementation is what `uv run stk new` writes
|
|
@@ -34,8 +34,8 @@ It never edits a stk file. Then:
|
|
|
34
34
|
## Conventions
|
|
35
35
|
|
|
36
36
|
The generated files already follow every stk convention. When you change them, the rules
|
|
37
|
-
are in `CLAUDE.md` and the details in https://docs.
|
|
38
|
-
https://docs.
|
|
37
|
+
are in `CLAUDE.md` and the details in https://docs.stkit.dev/models/, https://docs.stkit.dev/api/, and
|
|
38
|
+
https://docs.stkit.dev/frontend/. Read the page before editing that layer.
|
|
39
39
|
|
|
40
40
|
## Before you report done
|
|
41
41
|
|
|
@@ -26,7 +26,7 @@ wget -qO /tmp/deploy.sh https://raw.githubusercontent.com/level09/stk/master/dep
|
|
|
26
26
|
sudo DOMAIN=example.com REPO=<github-user>/<repo> bash /tmp/deploy.sh
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
Every variable and what the script installs: https://docs.
|
|
29
|
+
Every variable and what the script installs: https://docs.stkit.dev/deployment/.
|
|
30
30
|
|
|
31
31
|
Ends at a live `https://<domain>` with an admin login. Credentials are saved
|
|
32
32
|
on the server at `/home/<app-user>/.credentials`.
|
|
@@ -27,7 +27,7 @@ most three per run. Append and continue the task; never stop it to log.
|
|
|
27
27
|
|
|
28
28
|
Input: `.stk/lessons.md`, or the path the user gives.
|
|
29
29
|
|
|
30
|
-
1. **Drop what is covered.** Search the docs (https://docs.
|
|
30
|
+
1. **Drop what is covered.** Search the docs (https://docs.stkit.dev), `CLAUDE.md`, the skills, and
|
|
31
31
|
`stk/checks.py`. A lesson that recurs despite an existing rule means the rule sits too low:
|
|
32
32
|
move it up the ladder below instead of repeating it.
|
|
33
33
|
2. **Drop one-offs**: a single occurrence with no general cause.
|
|
@@ -58,7 +58,7 @@ Models with tables:
|
|
|
58
58
|
## Before you trust it
|
|
59
59
|
|
|
60
60
|
Model conventions and the three SQLite traps (non-transactional DDL, NOT NULL needs
|
|
61
|
-
`server_default`, named constraints) are in https://docs.
|
|
61
|
+
`server_default`, named constraints) are in https://docs.stkit.dev/models/. Check each exit code;
|
|
62
62
|
never pipe a migration command to `tail`, and never delete a revision file before
|
|
63
63
|
`uv run stk db downgrade <target>` has succeeded.
|
|
64
64
|
|