simple-module-cli 0.0.38__tar.gz → 0.0.40__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 (178) hide show
  1. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/PKG-INFO +3 -3
  2. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/pyproject.toml +3 -3
  3. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/git_source.py +19 -4
  4. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/skills/simple-module-conventions/SKILL.md +11 -0
  5. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/skills/simple-module-creating/SKILL.md +6 -0
  6. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/module/__PACKAGE__/service.py.tpl +7 -1
  7. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/update_cmd.py +2 -12
  8. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/tests/conftest.py +15 -0
  9. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/tests/test_cli_add_spec_parse.py +31 -0
  10. simple_module_cli-0.0.40/tests/test_cli_asset_prerequisites.py +19 -0
  11. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/tests/test_cli_openapi_gen_types.py +2 -2
  12. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/tests/test_cli_update_sources.py +35 -0
  13. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/tests/test_module_css_build.py +11 -8
  14. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/.gitignore +0 -0
  15. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/LICENSE +0 -0
  16. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/README.md +0 -0
  17. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/__init__.py +0 -0
  18. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/_add_install.py +0 -0
  19. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/_add_sources.py +0 -0
  20. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/_app_project_manifests.py +0 -0
  21. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/_env.py +0 -0
  22. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/_local_env.py +0 -0
  23. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/_module_build.py +0 -0
  24. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/_module_host.py +0 -0
  25. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/_templating.py +0 -0
  26. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/add_cmd.py +0 -0
  27. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/app_project.py +0 -0
  28. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/case.py +0 -0
  29. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/catalog.py +0 -0
  30. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/cli.py +0 -0
  31. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/docker_assets.py +0 -0
  32. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/docker_cmd.py +0 -0
  33. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/framework_repo.py +0 -0
  34. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/module_cmd.py +0 -0
  35. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/new.py +0 -0
  36. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/openapi_cmd.py +0 -0
  37. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/openapi_types.py +0 -0
  38. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/package_update.py +0 -0
  39. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/pins.py +0 -0
  40. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/plugins.py +0 -0
  41. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/pypi.py +0 -0
  42. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/pyproject_edit.py +0 -0
  43. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/recipes.py +0 -0
  44. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/requirements.py +0 -0
  45. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/scaffolding.py +0 -0
  46. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/skills/README.md +0 -0
  47. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/skills/simple-module-cli/SKILL.md +0 -0
  48. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/skills/simple-module-database/SKILL.md +0 -0
  49. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/skills/simple-module-doctor/SKILL.md +0 -0
  50. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/skills/simple-module-inertia-pages/SKILL.md +0 -0
  51. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/skills/simple-module-locales/SKILL.md +0 -0
  52. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/skills/simple-module-migrations/SKILL.md +0 -0
  53. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/skills/simple-module-registries/SKILL.md +0 -0
  54. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/skills/simple-module-testing/SKILL.md +0 -0
  55. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/skills_cmd.py +0 -0
  56. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/docker/Makefile.snippet +0 -0
  57. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/docker/compose-base-postgres.yml.tpl +0 -0
  58. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/docker/compose-base-sqlite.yml.tpl +0 -0
  59. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/docker/compose-tasks-postgres.yml.tpl +0 -0
  60. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/docker/compose-tasks-sqlite.yml.tpl +0 -0
  61. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/docker/compose-worker.yml.tpl +0 -0
  62. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/docker/dockerignore +0 -0
  63. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/docker/host-flat.Dockerfile +0 -0
  64. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/docker/host.Dockerfile +0 -0
  65. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/docker/worker.Dockerfile.tpl +0 -0
  66. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/host/.env.example +0 -0
  67. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/host/.gitignore +0 -0
  68. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/host/Makefile +0 -0
  69. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/host/README.md.tpl +0 -0
  70. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/host/_optional/background_tasks/Makefile.snippet +0 -0
  71. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/host/_optional/background_tasks/run_worker.py +0 -0
  72. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/host/alembic.ini +0 -0
  73. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/host/client_app/app.tsx +0 -0
  74. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/host/client_app/main.tsx +0 -0
  75. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/host/client_app/module-assets.ts +0 -0
  76. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/host/client_app/package.json.tpl +0 -0
  77. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/host/client_app/pages/Error.tsx +0 -0
  78. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/host/client_app/pages/Landing.tsx +0 -0
  79. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/host/client_app/pages.ts +0 -0
  80. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/host/client_app/styles.css +0 -0
  81. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/host/client_app/tsconfig.json +0 -0
  82. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/host/client_app/vite.config.ts +0 -0
  83. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/host/client_app/vite.dev-url.ts +0 -0
  84. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/host/main.py +0 -0
  85. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/host/migrations/env.py +0 -0
  86. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/host/migrations/script.py.mako +0 -0
  87. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/host/migrations/versions/.gitkeep +0 -0
  88. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/host/pyproject.toml.tpl +0 -0
  89. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/host/routes.py +0 -0
  90. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/host/templates/index.html +0 -0
  91. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/module/.github/workflows/ci.yml +0 -0
  92. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/module/.github/workflows/publish.yml.tpl +0 -0
  93. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/module/.gitignore +0 -0
  94. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/module/README.md.tpl +0 -0
  95. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/module/__PACKAGE__/__init__.py +0 -0
  96. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/module/__PACKAGE__/constants.py.tpl +0 -0
  97. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/module/__PACKAGE__/contracts/__init__.py.tpl +0 -0
  98. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/module/__PACKAGE__/contracts/schemas.py.tpl +0 -0
  99. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/module/__PACKAGE__/deps.py.tpl +0 -0
  100. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/module/__PACKAGE__/endpoints/__init__.py +0 -0
  101. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/module/__PACKAGE__/endpoints/api.py.tpl +0 -0
  102. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/module/__PACKAGE__/endpoints/views.py.tpl +0 -0
  103. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/module/__PACKAGE__/locales/en.json.tpl +0 -0
  104. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/module/__PACKAGE__/models.py.tpl +0 -0
  105. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/module/__PACKAGE__/module.py.tpl +0 -0
  106. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/module/__PACKAGE__/pages/Browse.tsx.tpl +0 -0
  107. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/module/__PACKAGE__/pages/Create.tsx.tpl +0 -0
  108. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/module/__PACKAGE__/pages/Edit.tsx.tpl +0 -0
  109. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/module/__PACKAGE__/pages/api.generated.ts.tpl +0 -0
  110. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/module/__PACKAGE__/pages/api.ts.tpl +0 -0
  111. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/module/__PACKAGE__/py.typed +0 -0
  112. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/module/__PACKAGE__/services.py.tpl +0 -0
  113. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/module/__PACKAGE__/settings.py.tpl +0 -0
  114. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/module/_optional/framework_repo/LICENSE +0 -0
  115. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/module/_optional/framework_repo/README.md.tpl +0 -0
  116. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/module/_optional/framework_repo/__PACKAGE__/pages/Browse.tsx.tpl +0 -0
  117. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/module/_optional/framework_repo/__PACKAGE__/pages/Create.tsx.tpl +0 -0
  118. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/module/_optional/framework_repo/__PACKAGE__/pages/Edit.tsx.tpl +0 -0
  119. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/module/_optional/framework_repo/pyproject.toml.tpl +0 -0
  120. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/module/_optional/standalone/package.json.tpl +0 -0
  121. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/module/_optional/standalone/tsconfig.json.tpl +0 -0
  122. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/module/package.json.tpl +0 -0
  123. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/module/pyproject.toml.tpl +0 -0
  124. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/module/tests/test___PACKAGE__.py.tpl +0 -0
  125. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/module/tsconfig.json.tpl +0 -0
  126. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/workspace/.env.example +0 -0
  127. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/workspace/.gitignore +0 -0
  128. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/workspace/Makefile +0 -0
  129. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/workspace/README.md.tpl +0 -0
  130. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/workspace/package.json.tpl +0 -0
  131. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/templates/workspace/pyproject.toml.tpl +0 -0
  132. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/uv_workspace.py +0 -0
  133. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/simple_module_cli/wizard.py +0 -0
  134. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/tests/test_build_packaging.py +0 -0
  135. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/tests/test_case.py +0 -0
  136. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/tests/test_cli_add_cmd.py +0 -0
  137. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/tests/test_cli_add_workspace_member.py +0 -0
  138. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/tests/test_cli_catalog.py +0 -0
  139. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/tests/test_cli_catalog_depends_on.py +0 -0
  140. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/tests/test_cli_docker_assets.py +0 -0
  141. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/tests/test_cli_docker_workers.py +0 -0
  142. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/tests/test_cli_gen_types_scaffold.py +0 -0
  143. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/tests/test_cli_git_scan.py +0 -0
  144. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/tests/test_cli_new.py +0 -0
  145. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/tests/test_cli_new_dest_tolerance.py +0 -0
  146. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/tests/test_cli_new_git_module.py +0 -0
  147. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/tests/test_cli_new_migration_bootstrap.py +0 -0
  148. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/tests/test_cli_new_regressions.py +0 -0
  149. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/tests/test_cli_new_scaffold_layout.py +0 -0
  150. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/tests/test_cli_openapi_export.py +0 -0
  151. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/tests/test_cli_package_update.py +0 -0
  152. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/tests/test_cli_package_update_pins.py +0 -0
  153. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/tests/test_cli_pyproject_edit.py +0 -0
  154. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/tests/test_cli_recipes.py +0 -0
  155. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/tests/test_cli_vite_port_env.py +0 -0
  156. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/tests/test_cli_wizard.py +0 -0
  157. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/tests/test_cli_wizard_git.py +0 -0
  158. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/tests/test_create_module_ci_skip.py +0 -0
  159. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/tests/test_env_helper.py +0 -0
  160. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/tests/test_framework_repo_wiring.py +0 -0
  161. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/tests/test_module_cmd_build.py +0 -0
  162. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/tests/test_module_cmd_e2e.py +0 -0
  163. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/tests/test_module_cmd_verify.py +0 -0
  164. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/tests/test_module_css_assets.py +0 -0
  165. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/tests/test_module_css_subdir_sources.py +0 -0
  166. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/tests/test_module_npm_aliases.py +0 -0
  167. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/tests/test_module_pages_manifest.py +0 -0
  168. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/tests/test_no_framework_deps.py +0 -0
  169. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/tests/test_plugin_discovery.py +0 -0
  170. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/tests/test_scaffold_app_tsx_spa_links.py +0 -0
  171. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/tests/test_scaffold_rollback.py +0 -0
  172. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/tests/test_scaffolded_host_setup_wizard.py +0 -0
  173. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/tests/test_scaffolding_framework_repo.py +0 -0
  174. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/tests/test_scaffolding_host.py +0 -0
  175. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/tests/test_scaffolding_module.py +0 -0
  176. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/tests/test_scaffolding_module_js.py +0 -0
  177. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/tests/test_skills_cmd.py +0 -0
  178. {simple_module_cli-0.0.38 → simple_module_cli-0.0.40}/tests/test_template_requires_framework.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: simple_module_cli
3
- Version: 0.0.38
3
+ Version: 0.0.40
4
4
  Summary: Standalone scaffolder for the SimpleModule framework — `smpy new`, `smpy create-module`, plugin host.
5
5
  Project-URL: Homepage, https://github.com/antosubash/simple_module_python
6
6
  Project-URL: Repository, https://github.com/antosubash/simple_module_python
@@ -19,8 +19,8 @@ Classifier: Topic :: Software Development :: Code Generators
19
19
  Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
20
20
  Classifier: Typing :: Typed
21
21
  Requires-Python: >=3.12
22
- Requires-Dist: tomlkit>=0.13
23
- Requires-Dist: typer>=0.12
22
+ Requires-Dist: tomlkit<0.16,>=0.13
23
+ Requires-Dist: typer<0.28,>=0.12
24
24
  Description-Content-Type: text/markdown
25
25
 
26
26
  # simple_module_cli
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "simple_module_cli"
3
- version = "0.0.38"
3
+ version = "0.0.40"
4
4
  description = "Standalone scaffolder for the SimpleModule framework — `smpy new`, `smpy create-module`, plugin host."
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -20,8 +20,8 @@ classifiers = [
20
20
  "Typing :: Typed",
21
21
  ]
22
22
  dependencies = [
23
- "typer>=0.12",
24
- "tomlkit>=0.13",
23
+ "typer>=0.12,<0.28",
24
+ "tomlkit>=0.13,<0.16",
25
25
  ]
26
26
 
27
27
  [project.scripts]
@@ -11,12 +11,14 @@ tomlkit alone (see test_no_framework_deps.py); clones are read with tomllib.
11
11
 
12
12
  from __future__ import annotations
13
13
 
14
+ import re
14
15
  import subprocess
15
16
  import tomllib
16
17
  from collections.abc import Callable, Iterable
17
18
  from dataclasses import dataclass
18
19
  from pathlib import Path
19
20
  from typing import Literal
21
+ from urllib.parse import urlsplit
20
22
 
21
23
  __all__ = [
22
24
  "FoundModule",
@@ -84,9 +86,8 @@ def _parse_git(raw: str) -> GitAddSpec:
84
86
  )
85
87
  ref: str | None = None
86
88
  head, sep, tail = body.rpartition("@")
87
- # `@` also appears in URL userinfo (ssh://git@host/...). A ref candidate
88
- # never contains "/" and what precedes it must still be a full URL.
89
- if sep and tail and "/" not in tail and "://" in head:
89
+ # Only an @ in the URL path separates a ref; userinfo belongs to the authority.
90
+ if sep and tail and "://" in head and "@" in urlsplit(body).path:
90
91
  body, ref = head, tail
91
92
  if "://" not in body:
92
93
  raise SpecError(f"cannot parse a git URL out of {raw!r}")
@@ -114,7 +115,21 @@ def _cmp(a: str, b: str) -> int:
114
115
  width = max(len(ta), len(tb))
115
116
  ta += (0,) * (width - len(ta))
116
117
  tb += (0,) * (width - len(tb))
117
- return (ta > tb) - (ta < tb)
118
+ if ta != tb:
119
+ return (ta > tb) - (ta < tb)
120
+ pa, pb = _prerelease_key(a), _prerelease_key(b)
121
+ return (pa > pb) - (pa < pb)
122
+
123
+
124
+ def _prerelease_key(version: str) -> tuple[int, int]:
125
+ # shortcut: handles Python a/b/rc suffixes; use a full parser for other version schemes.
126
+ match = re.search(r"(alpha|beta|a|b|rc)(\d*)$", version, re.IGNORECASE)
127
+ if match is None:
128
+ return (3, 0)
129
+ return (
130
+ {"a": 0, "alpha": 0, "b": 1, "beta": 1, "rc": 2}[match[1].lower()],
131
+ int(match[2] or "0"),
132
+ )
118
133
 
119
134
 
120
135
  _OPS = ("<=", ">=", "==", "!=", "<", ">")
@@ -121,6 +121,17 @@ Build a `MailMessage(to, subject, text)` and send it with `current_mail_sender()
121
121
 
122
122
  Which module holds the accounts depends on the auth provider (`users` or `keycloak`). To name, look up, search or count users, use `get_user_directory(app)` from `simple_module_core.user_directory`: `get_many(db, ids, with_roles=...)`, `search(db, term, limit=...)`, `match_ids(term)` (a subquery) and `stats(db, active_window=...)`. Name a user with `DirectoryUser.label`. `roles is None` means the provider does not know them (Keycloak) — refuse anything that needs roles rather than treating the user as role-less. Importing `users.models` from another module silently queries an empty table under Keycloak.
123
123
 
124
+ ### 13. Raise domain exceptions from services
125
+
126
+ Define a per-module base error in `<module>/exceptions.py` or `errors.py` and
127
+ subclass it for business failures. Services must not raise `HTTPException`:
128
+ CLI commands, tasks and durable handlers need transport-independent failures.
129
+ Follow `file_storage`: map errors in endpoints or install HTTP handlers through
130
+ `register_exception_handlers(app)`. Preserve status codes, exact JSON bodies,
131
+ headers and translation keys when migrating; translate at the request boundary.
132
+ Endpoint-level `HTTPException` for pure request validation is fine. Inertia
133
+ forms may catch domain errors to report field errors or redirect.
134
+
124
135
  ## Related skills
125
136
 
126
137
  - **simple-module-database** — per-module Base, mixins, session lifecycle
@@ -129,4 +129,10 @@ The new module should appear in the registered-modules log line. If it has a vie
129
129
  - **`name=` collides.** Two modules with the same `ModuleMeta.name` raise `SM008` at dev boot / `make doctor` (their lowercased names would also collide as table prefixes in the shared schema).
130
130
  - **Registered the module by hand in host code.** Don't — discovery is entry-point-only; host code never imports module code.
131
131
 
132
+ Services raise domain exceptions derived from a per-module base in
133
+ `exceptions.py` or `errors.py`, never `HTTPException`. Map them in endpoints or
134
+ `register_exception_handlers(app)` (as in `file_storage`); translate at the
135
+ request boundary. Preserve existing status codes, JSON bodies and headers.
136
+ Endpoint-level `HTTPException` for pure request validation remains fine.
137
+
132
138
  For framework-wide rules that apply once the module exists (SQLModel-everywhere, file-size cap, settings layout, no `session.commit()` in services), see the **simple-module-conventions** skill. For migration mechanics see **simple-module-migrations**.
@@ -1,4 +1,10 @@
1
- """{{SINGULAR_CLASS}} service implementation."""
1
+ """{{SINGULAR_CLASS}} service implementation.
2
+
3
+ Business failures raise domain exceptions derived from a per-module base in
4
+ exceptions.py or errors.py, never HTTPException. Map them in endpoints or via
5
+ ModuleBase.register_exception_handlers; keep HTTP status, payload, headers and
6
+ translation at that boundary so this service also works in CLI/tasks.
7
+ """
2
8
 
3
9
  from __future__ import annotations
4
10
 
@@ -21,7 +21,6 @@ from simple_module_cli.git_source import (
21
21
  derive_range,
22
22
  list_remote_refs,
23
23
  pick_latest_tag,
24
- version_tuple,
25
24
  )
26
25
  from simple_module_cli.pyproject_edit import (
27
26
  dep_constraint,
@@ -41,11 +40,6 @@ def _groups(sources: dict[str, dict]) -> dict[str, list[str]]:
41
40
  return by_url
42
41
 
43
42
 
44
- def _older_tag(a: str, b: str) -> str:
45
- """The lower of two v-prefixed tags — every sibling must accept the shared tag."""
46
- return a if version_tuple(a[1:]) <= version_tuple(b[1:]) else b
47
-
48
-
49
43
  def run_sources_update(
50
44
  pyproject: Path,
51
45
  *,
@@ -87,12 +81,8 @@ def run_sources_update(
87
81
  if not tag_pinned:
88
82
  continue
89
83
  tags, _ = list_remote_refs(url, **git_kwargs)
90
- candidate: str | None = None
91
- for name in tag_pinned:
92
- best = pick_latest_tag(tags, dep_constraint(doc, name))
93
- if best is None:
94
- continue
95
- candidate = best if candidate is None else _older_tag(candidate, best)
84
+ constraint = ",".join(dep_constraint(doc, name) or "" for name in tag_pinned)
85
+ candidate = pick_latest_tag(tags, constraint)
96
86
  if candidate is None:
97
87
  continue
98
88
  current = {str(sources[n]["tag"]) for n in tag_pinned}
@@ -7,6 +7,7 @@ how the factory below gets shared.
7
7
 
8
8
  from __future__ import annotations
9
9
 
10
+ import os
10
11
  import subprocess
11
12
  import sys
12
13
  from pathlib import Path
@@ -154,3 +155,17 @@ def make_git_module_repo(tmp_path: Path):
154
155
  return repo
155
156
 
156
157
  return factory
158
+
159
+
160
+ @pytest.fixture
161
+ def require_cli_asset():
162
+ """Keep optional local tooling from silently reducing CI coverage."""
163
+
164
+ def require(available: bool, message: str) -> None:
165
+ if available:
166
+ return
167
+ if os.environ.get("CI"):
168
+ pytest.fail(f"{message}. On CI this must fail rather than skip.")
169
+ pytest.skip(message)
170
+
171
+ return require
@@ -50,6 +50,16 @@ def test_git_ssh_userinfo_with_ref() -> None:
50
50
  assert p.git.ref == "main"
51
51
 
52
52
 
53
+ @pytest.mark.parametrize("scheme", ["https://", "ssh://git@"])
54
+ def test_git_spec_ref_with_slash(scheme: str) -> None:
55
+ url = f"{scheme}github.com/x/repo.git"
56
+ p = parse_add_spec(f"git+{url}@feature/new#subdirectory=modules/blog")
57
+ assert p.git is not None
58
+ assert p.git.url == url
59
+ assert p.git.ref == "feature/new"
60
+ assert p.git.subdirectory == "modules/blog"
61
+
62
+
53
63
  def test_path_spec() -> None:
54
64
  p = parse_add_spec("../mod")
55
65
  assert p.kind == "path"
@@ -89,3 +99,24 @@ def test_pick_latest_tag() -> None:
89
99
  assert pick_latest_tag(tags, ">=1.0,<2.0") == "v1.10.0"
90
100
  assert pick_latest_tag(tags, None) == "v2.0.0"
91
101
  assert pick_latest_tag(["nope"], None) is None
102
+
103
+
104
+ @pytest.mark.parametrize("prerelease", ["1.1.0a1", "1.1.0b1", "1.1.0rc1"])
105
+ def test_prerelease_does_not_satisfy_final_floor(prerelease: str) -> None:
106
+ assert not satisfies(prerelease, ">=1.1.0")
107
+ assert not satisfies(prerelease, "==1.1.0")
108
+ assert satisfies(prerelease, "<1.1.0")
109
+
110
+
111
+ @pytest.mark.parametrize(
112
+ ("tags", "expected"),
113
+ [
114
+ (["v1.1.0rc1", "v1.1.0"], "v1.1.0"),
115
+ (["v1.1.0rc2", "v1.1.0rc10"], "v1.1.0rc10"),
116
+ (["v1.1.0a1", "v1.1.0b1", "v1.1.0rc1"], "v1.1.0rc1"),
117
+ (["v1.1.0alpha2", "v1.1.0beta1", "v1.1.0"], "v1.1.0"),
118
+ (["v1.1.0alpha2", "v1.1.0beta1"], "v1.1.0beta1"),
119
+ ],
120
+ )
121
+ def test_pick_latest_tag_orders_prereleases(tags: list[str], expected: str) -> None:
122
+ assert pick_latest_tag(tags, None) == expected
@@ -0,0 +1,19 @@
1
+ """Missing frontend prerequisites may skip locally but must fail on CI."""
2
+
3
+ import pytest
4
+
5
+
6
+ @pytest.mark.parametrize("ci", [None, "true"])
7
+ def test_cli_missing_asset_prerequisite_fails_on_ci(monkeypatch, require_cli_asset, ci):
8
+ if ci:
9
+ monkeypatch.setenv("CI", ci)
10
+ else:
11
+ monkeypatch.delenv("CI", raising=False)
12
+ outcome = pytest.fail.Exception if ci else pytest.skip.Exception
13
+ with pytest.raises(outcome, match="missing tool"):
14
+ require_cli_asset(False, "missing tool")
15
+
16
+
17
+ def test_cli_available_asset_prerequisite_runs_on_ci(monkeypatch, require_cli_asset):
18
+ monkeypatch.setenv("CI", "true")
19
+ require_cli_asset(True, "missing tool")
@@ -185,8 +185,8 @@ def test_pinned_version_matches_the_ui_dev_dependency() -> None:
185
185
  assert ui["devDependencies"]["openapi-typescript"] == ot.OPENAPI_TYPESCRIPT_VERSION
186
186
 
187
187
 
188
- @pytest.mark.skipif(not LOCAL_TOOL.is_file(), reason="needs `npm install` (openapi-typescript)")
189
- def test_real_tool_emits_paths(importable: Path) -> None:
188
+ def test_real_tool_emits_paths(importable: Path, require_cli_asset) -> None:
189
+ require_cli_asset(LOCAL_TOOL.is_file(), "needs `npm ci` (openapi-typescript)")
190
190
  targets = ot.module_targets(
191
191
  [_fake_module(importable, "gt_real", "Widgets", opted_in=True)], importable, []
192
192
  )
@@ -4,6 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  from pathlib import Path
6
6
 
7
+ import pytest
7
8
  from simple_module_cli.update_cmd import run_sources_update
8
9
 
9
10
 
@@ -125,3 +126,37 @@ def test_no_newer_tag_is_a_quiet_noop(tmp_path: Path, capsys) -> None:
125
126
  )
126
127
  assert p.read_text(encoding="utf-8") == before
127
128
  assert "up to date" in capsys.readouterr().out
129
+
130
+
131
+ @pytest.mark.parametrize(
132
+ ("blog_range", "comments_range", "expected"),
133
+ [
134
+ (">=0.3,<1.0", ">=0.1,<0.3", None), # disjoint ranges
135
+ (">=0.1,<1.0,!=0.3.0", ">=0.1,<0.4", "v0.2.0"), # hole below best tags
136
+ (">=0.1,<1.0", ">=0.8,<1.0", None), # one sibling has no matching tag
137
+ ],
138
+ )
139
+ def test_group_update_requires_tag_accepted_by_every_sibling(
140
+ tmp_path: Path, blog_range: str, comments_range: str, expected: str | None
141
+ ) -> None:
142
+ p = _host(tmp_path, "https://github.com/x/mods")
143
+ before = (
144
+ p.read_text(encoding="utf-8")
145
+ .replace("simple_module_blog>=0.1.0,<1.0", f"simple_module_blog{blog_range}")
146
+ .replace("simple_module_comments>=0.1.0,<1.0", f"simple_module_comments{comments_range}")
147
+ )
148
+ p.write_text(before, encoding="utf-8")
149
+ calls, runner = _recorder()
150
+ run_sources_update(
151
+ p,
152
+ only="simple_module_blog",
153
+ dry_run=False,
154
+ git_runner=_git_stub(["v0.1.0", "v0.2.0", "v0.3.0", "v0.5.0"]),
155
+ exec_runner=runner,
156
+ )
157
+ after = p.read_text(encoding="utf-8")
158
+ if expected is None:
159
+ assert after == before
160
+ assert calls == []
161
+ else:
162
+ assert after.count(f'tag = "{expected}"') == 2
@@ -6,9 +6,8 @@ resolves the path and keeps the rule. This is the one assertion that exercises
6
6
  that whole chain, so it drives a genuine build rather than inspecting the
7
7
  generated text.
8
8
 
9
- Skipped where a build is impossible: CI's `python-tests` job installs Python
10
- deps only (`make install-py`), so `node_modules` is absent there. The JS jobs
11
- and local runs do have it.
9
+ Missing build prerequisites skip locally and fail on CI, where the Python
10
+ and JS build jobs install the required Node dependencies.
12
11
  """
13
12
 
14
13
  from __future__ import annotations
@@ -22,17 +21,21 @@ import pytest
22
21
  REPO_ROOT = Path(__file__).resolve().parents[3]
23
22
 
24
23
  pytestmark = [
25
- pytest.mark.skipif(
26
- not (REPO_ROOT / "node_modules").is_dir(),
27
- reason="node_modules absent — run `npm install` to exercise the real build",
28
- ),
29
- pytest.mark.skipif(shutil.which("npm") is None, reason="npm not on PATH"),
30
24
  # Both tests rewrite the shared tree (generated manifests, host/static/dist, a
31
25
  # probe page under audit_log), so under xdist they must share one worker.
32
26
  pytest.mark.xdist_group("repo-build"),
33
27
  ]
34
28
 
35
29
 
30
+ @pytest.fixture(autouse=True)
31
+ def require_build_tools(require_cli_asset):
32
+ require_cli_asset(shutil.which("npm") is not None, "npm not on PATH")
33
+ require_cli_asset(
34
+ (REPO_ROOT / "node_modules").is_dir(),
35
+ "node_modules absent — run `npm ci` to exercise the real build",
36
+ )
37
+
38
+
36
39
  class TestModuleCssReachesBundle:
37
40
  def test_module_class_is_emitted_by_vite_build(self):
38
41
  """A class defined only in a module's styles.css appears in the built CSS.