splent-cli 1.23.0__tar.gz → 1.24.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (195) hide show
  1. {splent_cli-1.23.0/src/splent_cli.egg-info → splent_cli-1.24.0}/PKG-INFO +4 -1
  2. {splent_cli-1.23.0 → splent_cli-1.24.0}/pyproject.toml +10 -1
  3. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/cli.py +14 -1
  4. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/check/check_env.py +7 -4
  5. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/check/check_infra.py +21 -5
  6. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/database/db_migrate.py +34 -14
  7. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/database/db_status.py +4 -2
  8. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/export/export_puml.py +1 -1
  9. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_contract.py +11 -0
  10. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_impact.py +1 -1
  11. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_refine.py +115 -23
  12. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_release.py +111 -5
  13. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_test.py +92 -1
  14. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/marketplace/marketplace_index.py +1 -1
  15. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_configure.py +1 -1
  16. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_containers.py +4 -1
  17. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_create.py +4 -4
  18. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_deploy.py +21 -19
  19. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_derive.py +31 -7
  20. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_down.py +12 -4
  21. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_release.py +14 -1
  22. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_shell.py +21 -1
  23. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/release/release_core.py +10 -1
  24. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/release/release_resume.py +29 -2
  25. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/spl/spl_add_feature.py +15 -6
  26. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/tokens_setup.py +4 -0
  27. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/services/compose.py +12 -2
  28. splent_cli-1.24.0/src/splent_cli/services/pypi_quota.py +538 -0
  29. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/services/registry.py +16 -10
  30. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/services/release.py +130 -29
  31. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/services/release_gate.py +42 -23
  32. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/services/spl_store.py +33 -3
  33. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/utils/forge.py +11 -4
  34. {splent_cli-1.23.0 → splent_cli-1.24.0/src/splent_cli.egg-info}/PKG-INFO +4 -1
  35. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli.egg-info/SOURCES.txt +1 -0
  36. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli.egg-info/requires.txt +3 -0
  37. {splent_cli-1.23.0 → splent_cli-1.24.0}/LICENSE +0 -0
  38. {splent_cli-1.23.0 → splent_cli-1.24.0}/README.md +0 -0
  39. {splent_cli-1.23.0 → splent_cli-1.24.0}/setup.cfg +0 -0
  40. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/__init__.py +0 -0
  41. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/__main__.py +0 -0
  42. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/__init__.py +0 -0
  43. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/cache/__init__.py +0 -0
  44. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/cache/cache_clear.py +0 -0
  45. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/cache/cache_orphans.py +0 -0
  46. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/cache/cache_outdated.py +0 -0
  47. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/cache/cache_prune.py +0 -0
  48. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/cache/cache_size.py +0 -0
  49. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/cache/cache_status.py +0 -0
  50. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/cache/cache_usage.py +0 -0
  51. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/cache/cache_versions.py +0 -0
  52. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/check/__init__.py +0 -0
  53. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/check/check_contracts.py +0 -0
  54. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/check/check_deps.py +0 -0
  55. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/check/check_docker.py +0 -0
  56. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/check/check_features.py +0 -0
  57. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/check/check_github.py +0 -0
  58. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/check/check_product.py +0 -0
  59. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/check/check_pypi.py +0 -0
  60. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/check/check_pyproject.py +0 -0
  61. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/check/check_releases.py +0 -0
  62. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/clear/__init__.py +0 -0
  63. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/clear/clear_build.py +0 -0
  64. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/clear/clear_log.py +0 -0
  65. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/clear/clear_uploads.py +0 -0
  66. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/command/__init__.py +0 -0
  67. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/command/command_create.py +0 -0
  68. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/coverage.py +0 -0
  69. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/database/db_console.py +0 -0
  70. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/database/db_dump.py +0 -0
  71. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/database/db_reset.py +0 -0
  72. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/database/db_restore.py +0 -0
  73. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/database/db_rollback.py +0 -0
  74. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/database/db_seed.py +0 -0
  75. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/database/db_upgrade.py +0 -0
  76. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/doctor.py +0 -0
  77. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/env/env_list.py +0 -0
  78. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/env/env_set.py +0 -0
  79. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/env/env_show.py +0 -0
  80. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/export/__init__.py +0 -0
  81. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_add.py +0 -0
  82. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_attach.py +0 -0
  83. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_clean.py +0 -0
  84. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_clone.py +0 -0
  85. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_compat.py +0 -0
  86. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_compile.py +0 -0
  87. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_create.py +0 -0
  88. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_delete.py +0 -0
  89. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_detach.py +0 -0
  90. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_discard.py +0 -0
  91. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_drift.py +0 -0
  92. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_env.py +0 -0
  93. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_fork.py +0 -0
  94. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_git.py +0 -0
  95. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_hook_add.py +0 -0
  96. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_hook_remove.py +0 -0
  97. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_hooks.py +0 -0
  98. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_info.py +0 -0
  99. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_inject_config.py +0 -0
  100. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_install.py +0 -0
  101. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_list.py +0 -0
  102. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_order.py +0 -0
  103. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_outdated.py +0 -0
  104. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_pin.py +0 -0
  105. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_pip_install.py +0 -0
  106. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_pull.py +0 -0
  107. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_remove.py +0 -0
  108. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_rename.py +0 -0
  109. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_search.py +0 -0
  110. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_status.py +0 -0
  111. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_sync_template.py +0 -0
  112. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_translate.py +0 -0
  113. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_unlock.py +0 -0
  114. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_upgrade.py +0 -0
  115. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_versions.py +0 -0
  116. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_xray.py +0 -0
  117. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/lint.py +0 -0
  118. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/locust.py +0 -0
  119. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/marketplace/login.py +0 -0
  120. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/marketplace/logout.py +0 -0
  121. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/marketplace/marketplace_auth_utils.py +0 -0
  122. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/marketplace/whoami.py +0 -0
  123. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/__init__.py +0 -0
  124. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_auto_require.py +0 -0
  125. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_build.py +0 -0
  126. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_clean.py +0 -0
  127. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_commands.py +0 -0
  128. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_config.py +0 -0
  129. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_console.py +0 -0
  130. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_deselect.py +0 -0
  131. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_drift.py +0 -0
  132. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_env.py +0 -0
  133. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_list.py +0 -0
  134. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_logs.py +0 -0
  135. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_missing.py +0 -0
  136. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_port.py +0 -0
  137. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_resolve.py +0 -0
  138. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_restart.py +0 -0
  139. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_routes.py +0 -0
  140. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_run.py +0 -0
  141. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_select.py +0 -0
  142. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_signals.py +0 -0
  143. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_sync_template.py +0 -0
  144. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_test.py +0 -0
  145. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_up.py +0 -0
  146. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_validate.py +0 -0
  147. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/release/__init__.py +0 -0
  148. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/selenium.py +0 -0
  149. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/spl/__init__.py +0 -0
  150. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/spl/spl_add_constraints.py +0 -0
  151. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/spl/spl_compare.py +0 -0
  152. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/spl/spl_configurations.py +0 -0
  153. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/spl/spl_create.py +0 -0
  154. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/spl/spl_deps.py +0 -0
  155. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/spl/spl_features.py +0 -0
  156. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/spl/spl_fetch.py +0 -0
  157. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/spl/spl_info.py +0 -0
  158. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/spl/spl_list.py +0 -0
  159. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/spl/spl_migrate_catalog.py +0 -0
  160. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/spl/spl_outdated.py +0 -0
  161. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/spl/spl_pin.py +0 -0
  162. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/spl/spl_publish.py +0 -0
  163. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/spl/spl_remove_feature.py +0 -0
  164. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/spl/spl_utils.py +0 -0
  165. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/uvl/uvl_utils.py +0 -0
  166. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/version.py +0 -0
  167. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/services/__init__.py +0 -0
  168. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/services/context.py +0 -0
  169. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/services/credentials.py +0 -0
  170. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/services/index_refresh.py +0 -0
  171. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/services/marketplace.py +0 -0
  172. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/services/marketplace_api.py +0 -0
  173. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/services/marketplace_url.py +0 -0
  174. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/services/preflight.py +0 -0
  175. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/utils/__init__.py +0 -0
  176. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/utils/archetype.py +0 -0
  177. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/utils/cache_utils.py +0 -0
  178. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/utils/command_loader.py +0 -0
  179. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/utils/contract_freshness.py +0 -0
  180. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/utils/db_utils.py +0 -0
  181. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/utils/decorators.py +0 -0
  182. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/utils/dynamic_imports.py +0 -0
  183. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/utils/feature_installer.py +0 -0
  184. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/utils/feature_utils.py +0 -0
  185. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/utils/git_url.py +0 -0
  186. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/utils/integrity.py +0 -0
  187. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/utils/io_utils.py +0 -0
  188. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/utils/lifecycle.py +0 -0
  189. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/utils/manifest.py +0 -0
  190. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/utils/path_utils.py +0 -0
  191. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/utils/proc.py +0 -0
  192. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/utils/template_drift.py +0 -0
  193. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli.egg-info/dependency_links.txt +0 -0
  194. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli.egg-info/entry_points.txt +0 -0
  195. {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: splent_cli
3
- Version: 1.23.0
3
+ Version: 1.24.0
4
4
  Summary: SPLENT-CLI is a CLI to be able to work on your development more easily.
5
5
  Author-email: DiversoLab <diversolab@us.es>
6
6
  Project-URL: Homepage, https://github.com/diverso-lab/splent_cli
@@ -9,9 +9,12 @@ Requires-Python: >=3.13
9
9
  Description-Content-Type: text/markdown
10
10
  License-File: LICENSE
11
11
  Requires-Dist: click==8.1.8
12
+ Requires-Dist: jinja2>=3.1
12
13
  Requires-Dist: packaging>=24.0
14
+ Requires-Dist: python-dotenv>=1.0
13
15
  Requires-Dist: pyyaml>=6.0
14
16
  Requires-Dist: requests>=2.31.0
17
+ Requires-Dist: splent_framework<2,>=1.15.0
15
18
  Requires-Dist: tomli_w>=1.0.0
16
19
  Provides-Extra: dev
17
20
  Requires-Dist: setuptools==80.3.1; extra == "dev"
@@ -4,18 +4,27 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "splent_cli"
7
- version = "1.23.0"
7
+ version = "1.24.0"
8
8
  description = "SPLENT-CLI is a CLI to be able to work on your development more easily."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.13"
11
11
  authors = [{ name = "DiversoLab", email = "diversolab@us.es" }]
12
12
  license-files = ["LICENSE"]
13
13
 
14
+ # Everything the CLI imports at module level must be listed here (or come
15
+ # with splent_framework, see tests/unit/test_declared_dependencies.py):
16
+ # startup imports every command module, so one undeclared import breaks
17
+ # `splent --help` on a clean `pip install splent_cli`.
14
18
  dependencies = [
15
19
  "click==8.1.8",
20
+ "jinja2>=3.1",
16
21
  "packaging>=24.0",
22
+ "python-dotenv>=1.0",
17
23
  "pyyaml>=6.0",
18
24
  "requests>=2.31.0",
25
+ # The CLI drives the framework (paths, feature order, migrations, the
26
+ # app factory) and only works with the same major version of it.
27
+ "splent_framework>=1.15.0,<2",
19
28
  "tomli_w>=1.0.0",
20
29
  ]
21
30
 
@@ -1,3 +1,4 @@
1
+ import logging
1
2
  import os
2
3
 
3
4
  import click
@@ -37,6 +38,15 @@ class SPLENTCLI(click.Group):
37
38
  return self._feature_cmds_cache
38
39
 
39
40
  self._feature_cmds_cache: dict[str, click.BaseCommand] = {}
41
+ # Discovery runs before every command, including the ones a product
42
+ # that is not built yet (selected, not configured or derived) runs
43
+ # while getting there. Building its app then fails, and the framework
44
+ # logged that failure with a full traceback on every single command,
45
+ # although nothing was wrong. The failure is expected here and
46
+ # handled below, so the framework's logging is muted for the attempt.
47
+ debug = bool(os.getenv("SPLENT_DEBUG"))
48
+ if not debug:
49
+ logging.disable(logging.CRITICAL)
40
50
  try:
41
51
  from splent_cli.utils.dynamic_imports import get_app
42
52
 
@@ -57,10 +67,13 @@ class SPLENTCLI(click.Group):
57
67
  group.add_command(cmd)
58
68
  self._feature_cmds_cache[group.name] = group
59
69
  except Exception as e:
60
- if os.getenv("SPLENT_DEBUG"):
70
+ if debug:
61
71
  click.secho(
62
72
  f" ⚠ Feature commands not loaded: {e}", fg="yellow", err=True
63
73
  )
74
+ finally:
75
+ if not debug:
76
+ logging.disable(logging.NOTSET)
64
77
  return self._feature_cmds_cache
65
78
 
66
79
  def get_command(self, ctx, cmd_name):
@@ -8,6 +8,8 @@ import sys
8
8
  import click
9
9
  import importlib.metadata
10
10
 
11
+ from splent_cli.utils import forge
12
+
11
13
 
12
14
  def _pkg_version(name: str) -> str | None:
13
15
  try:
@@ -88,11 +90,12 @@ def check_env():
88
90
  f"CLI={'?' if not cli_v else cli_v} / Framework={'?' if not fw_v else fw_v}"
89
91
  )
90
92
 
91
- # Credentials
92
- if os.getenv("GITHUB_TOKEN"):
93
- _ok("GITHUB_TOKEN set")
93
+ # Credentials. The forge token is named after the forge in use: asking for
94
+ # GITHUB_TOKEN on a git.splent.io workspace sent people to the wrong place.
95
+ if forge.token():
96
+ _ok(f"{forge.token_env_name()} set ({forge.host()})")
94
97
  else:
95
- _warn("GITHUB_TOKEN not set")
98
+ _warn(f"{forge.token_env_name()} not set ({forge.host()})")
96
99
 
97
100
  if os.getenv("TWINE_PASSWORD") or os.getenv("PYPI_TOKEN"):
98
101
  _ok("PyPI token set")
@@ -382,17 +382,33 @@ def check_infra():
382
382
  else:
383
383
  _ok(f"No port conflicts ({len(all_ports)} ports declared)")
384
384
 
385
- # Check against running containers (query docker ps once, not per-port)
385
+ # Check against running containers (query docker ps once, not per-port).
386
+ # The product's own stacks are not conflicts: with the product up, every
387
+ # declared port is held by exactly the container meant to hold it, and
388
+ # reporting those as warnings made a healthy product look broken.
389
+ own_projects = {compose.project_name(product, env)} | {
390
+ compose.feature_project_name(compose.normalize_feature_ref(f), product, env)
391
+ for f in features
392
+ }
386
393
  running_conflicts = []
387
- ps_result = _run(["docker", "ps", "--format", "{{.ID}}\t{{.Names}}\t{{.Ports}}"])
394
+ ps_result = _run(
395
+ [
396
+ "docker",
397
+ "ps",
398
+ "--format",
399
+ '{{.ID}}\t{{.Names}}\t{{.Label "com.docker.compose.project"}}\t{{.Ports}}',
400
+ ]
401
+ )
388
402
  if ps_result.returncode == 0:
389
403
  ps_lines = ps_result.stdout.splitlines()
390
404
  for port in all_ports:
391
405
  for line in ps_lines:
392
- parts = line.split("\t", 2)
393
- if len(parts) < 3:
406
+ parts = line.split("\t", 3)
407
+ if len(parts) < 4:
408
+ continue
409
+ cid, name, project, ports_str = parts
410
+ if project in own_projects:
394
411
  continue
395
- cid, name, ports_str = parts
396
412
  if f":{port}->" in ports_str:
397
413
  running_conflicts.append((port, name))
398
414
 
@@ -1,4 +1,6 @@
1
1
  import os
2
+ import shutil
3
+ import subprocess
2
4
 
3
5
  import click
4
6
  from flask_migrate import migrate as alembic_migrate
@@ -46,10 +48,23 @@ def _is_empty_migration(path: str) -> bool:
46
48
  return True
47
49
 
48
50
 
51
+ def _format_migration(path: str) -> None:
52
+ """Run ``ruff format`` on a freshly generated migration, if ruff exists.
53
+
54
+ Alembic writes single quotes and one long create_table() call, which the
55
+ release gate's ``ruff format --check`` then rejects: the first release of
56
+ any feature with a model failed lint on a file nobody wrote by hand.
57
+ """
58
+ ruff = shutil.which("ruff")
59
+ if not ruff:
60
+ return
61
+ subprocess.run([ruff, "format", "--quiet", path], check=False)
62
+
63
+
49
64
  @requires_db
50
65
  @click.command(
51
66
  "db:migrate",
52
- short_help="Generate a new migration from model changes and apply it.",
67
+ short_help="Generate a new migration from model changes (db:upgrade applies it).",
53
68
  )
54
69
  @click.argument("feature", required=False, default=None)
55
70
  @click.option(
@@ -67,8 +82,8 @@ def _is_empty_migration(path: str) -> bool:
67
82
  @context.requires_product
68
83
  def db_migrate(feature, message, empty):
69
84
  """
70
- Generate new migration files for features that have schema changes,
71
- then apply all pending migrations.
85
+ Generate new migration files for features that have schema changes.
86
+ Nothing is applied: run ``splent db:upgrade`` for that.
72
87
 
73
88
  Empty migrations (no schema changes detected) are automatically removed,
74
89
  unless ``--empty`` is passed, in which case a blank migration is created
@@ -158,10 +173,23 @@ def db_migrate(feature, message, empty):
158
173
 
159
174
  after = _count_versions(mdir)
160
175
 
176
+ newest = None
177
+ if after > before:
178
+ versions_dir = os.path.join(mdir, "versions")
179
+ newest = max(
180
+ (
181
+ os.path.join(versions_dir, f)
182
+ for f in os.listdir(versions_dir)
183
+ if f.endswith(".py")
184
+ ),
185
+ key=os.path.getmtime,
186
+ )
187
+
161
188
  # A blank migration requested with --empty is intentional: keep it so the
162
189
  # user can fill in upgrade()/downgrade() by hand (e.g. refinement columns).
163
190
  if empty:
164
- if after > before:
191
+ if newest:
192
+ _format_migration(newest)
165
193
  click.echo(
166
194
  click.style(
167
195
  f" 📝 {feat}: blank migration generated — "
@@ -174,20 +202,12 @@ def db_migrate(feature, message, empty):
174
202
  continue
175
203
 
176
204
  # If a new file was generated, check if it's empty (no real schema changes)
177
- if after > before:
178
- versions_dir = os.path.join(mdir, "versions")
179
- newest = max(
180
- (
181
- os.path.join(versions_dir, f)
182
- for f in os.listdir(versions_dir)
183
- if f.endswith(".py")
184
- ),
185
- key=os.path.getmtime,
186
- )
205
+ if newest:
187
206
  if _is_empty_migration(newest):
188
207
  os.remove(newest)
189
208
  click.echo(click.style(f" ✔ {feat}: up to date", fg="green"))
190
209
  else:
210
+ _format_migration(newest)
191
211
  click.echo(
192
212
  click.style(f" 📝 {feat}: new migration generated", fg="cyan")
193
213
  )
@@ -104,8 +104,10 @@ def db_status():
104
104
  mdir = dirs.get(feat)
105
105
  fs_head = _get_filesystem_head(mdir) if mdir else None
106
106
 
107
- db_display = db_rev[:12] if db_rev else "—"
108
- fs_display = fs_head[:12] if fs_head else "—"
107
+ # Cut to the column, not to 12: a hex revision is 12 characters but a
108
+ # named one is not, and "auth0002_role" showed as "auth0002_rol".
109
+ db_display = db_rev[:col_rev] if db_rev else "—"
110
+ fs_display = fs_head[:col_rev] if fs_head else "—"
109
111
 
110
112
  if db_rev and fs_head and db_rev == fs_head:
111
113
  status = click.style("✔ synced", fg="green")
@@ -817,7 +817,7 @@ def export_puml(
817
817
  if not uvl_path:
818
818
  click.secho(
819
819
  "❌ No UVL file found.\n"
820
- " Set [tool.splent].spl in pyproject.toml (SPL catalog)\n"
820
+ " Set [tool.splent].spl in pyproject.toml (the SPL model)\n"
821
821
  " or [tool.splent.uvl].file (legacy).",
822
822
  fg="red",
823
823
  )
@@ -233,6 +233,17 @@ def _print_diff(current: dict, inferred: dict) -> bool:
233
233
  diff_lines.append(click.style(f" - archetype: {old_arch}", fg="red"))
234
234
  diff_lines.append(click.style(f" + archetype: {new_arch}", fg="green"))
235
235
 
236
+ # extensible.routes is a boolean; a flip alone is still a change to write
237
+ old_routes = bool(current.get("extensible_routes", False))
238
+ new_routes = bool(inferred.get("extensible_routes", False))
239
+ if old_routes != new_routes:
240
+ diff_lines.append(
241
+ click.style(
242
+ f" ~ extensible.routes: {old_routes} → {new_routes}",
243
+ fg="green" if new_routes else "red",
244
+ )
245
+ )
246
+
236
247
  for key, label in FIELDS:
237
248
  diff_lines.extend(
238
249
  _diff_field(label, current.get(key, []), inferred.get(key, []))
@@ -65,7 +65,7 @@ def feature_impact(feature_ref):
65
65
  click.secho(" pyproject.toml not found.", fg="red")
66
66
  raise SystemExit(1)
67
67
 
68
- uvl = _uvl_path(product_dir)
68
+ uvl = _uvl_path(product_dir, workspace)
69
69
  if not uvl or not os.path.isfile(uvl):
70
70
  click.secho(" No UVL file found — cannot compute impact.", fg="red")
71
71
  raise SystemExit(1)
@@ -54,6 +54,53 @@ def _is_stub(content: str) -> bool:
54
54
  return any(marker.lower() in lower for marker in _SCAFFOLD_MARKERS)
55
55
 
56
56
 
57
+ def _scaffold_renderings(path: str) -> set[str]:
58
+ """What feature:create writes at *path*, for every feature type.
59
+
60
+ The refiner is created with feature:create right before the wizard runs,
61
+ so its models.py, services.py, repositories.py and __init__.py are exactly
62
+ the scaffold, yet none of them carries a marker. The wizard used to ask
63
+ whether to overwrite each one, default No, and keeping the old __init__.py
64
+ left an import of a service the wizard had just replaced, so the app no
65
+ longer booted. Comparing against the rendered scaffold recognises them
66
+ without stamping a marker into files developers then edit.
67
+ """
68
+ from splent_cli.commands.feature.feature_create import (
69
+ FEATURE_TYPES,
70
+ _build_file_map,
71
+ setup_jinja_env,
72
+ )
73
+
74
+ parts = os.path.normpath(path).split(os.sep)
75
+ if "src" not in parts:
76
+ return set()
77
+ at = len(parts) - 1 - parts[::-1].index("src")
78
+ if len(parts) < at + 4:
79
+ return set()
80
+ org_safe, feature_name = parts[at + 1], parts[at + 2]
81
+ rel = "/".join(parts[at + 3 :])
82
+ short_name = feature_name.removeprefix("splent_feature_")
83
+ ctx = {
84
+ "feature_name": feature_name,
85
+ "short_name": short_name,
86
+ "pascal_name": "".join(w.capitalize() for w in short_name.split("_")),
87
+ "org_safe": org_safe,
88
+ "feature_import": f"{org_safe}.{feature_name}",
89
+ "env_prefix": short_name.upper(),
90
+ "host_port_ref": "${" + short_name.upper() + "_HOST_PORT}",
91
+ }
92
+ env = setup_jinja_env()
93
+ rendered = set()
94
+ for feature_type in FEATURE_TYPES:
95
+ template = _build_file_map(feature_type, short_name)[0].get(rel)
96
+ if template:
97
+ try:
98
+ rendered.add(env.get_template(template).render(ctx).strip())
99
+ except Exception:
100
+ continue
101
+ return rendered
102
+
103
+
57
104
  def _confirm_overwrite(path: str, label: str) -> bool:
58
105
  """Warn before overwriting a non-stub source file. Returns True if it is
59
106
  safe to write (file absent, a stub, or the user confirmed)."""
@@ -61,7 +108,7 @@ def _confirm_overwrite(path: str, label: str) -> bool:
61
108
  return True
62
109
  with open(path, "r", encoding="utf-8") as f:
63
110
  existing = f.read()
64
- if _is_stub(existing):
111
+ if _is_stub(existing) or existing.strip() in _scaffold_renderings(path):
65
112
  return True
66
113
  click.echo()
67
114
  click.secho(
@@ -301,17 +348,22 @@ class {mixin_name}:
301
348
  with open(models_path, "r") as f:
302
349
  content = f.read()
303
350
  if mixin_name in content:
304
- return
351
+ click.echo(
352
+ click.style(" kept: ", dim=True)
353
+ + f"models.py ({mixin_name} already there)"
354
+ )
355
+ return False
305
356
 
306
357
  # Warn before clobbering developer-authored models.py
307
358
  if not _confirm_overwrite(models_path, "models.py"):
308
359
  click.echo(click.style(" skipped: ", dim=True) + "models.py (kept)")
309
- return
360
+ return False
310
361
 
311
362
  # Overwrite — refinement features only need mixins, not db.Model classes
312
363
  os.makedirs(os.path.dirname(models_path), exist_ok=True)
313
364
  with open(models_path, "w") as f:
314
365
  f.write(mixin_code)
366
+ return True
315
367
 
316
368
 
317
369
  def _scaffold_service(
@@ -348,12 +400,16 @@ def _scaffold_service(
348
400
  with open(services_path, "r") as f:
349
401
  content = f.read()
350
402
  if replacement_name in content:
351
- return
403
+ click.echo(
404
+ click.style(" kept: ", dim=True)
405
+ + f"services.py ({replacement_name} already there)"
406
+ )
407
+ return False
352
408
 
353
409
  # Warn before clobbering developer-authored services.py
354
410
  if not _confirm_overwrite(services_path, "services.py"):
355
411
  click.echo(click.style(" skipped: ", dim=True) + "services.py (kept)")
356
- return
412
+ return False
357
413
 
358
414
  # Overwrite — refinement features only need replacement classes
359
415
  os.makedirs(os.path.dirname(services_path), exist_ok=True)
@@ -370,6 +426,7 @@ def _scaffold_service(
370
426
  "# Refinement features do not need their own repository.\n"
371
427
  "# The base feature's repository is used via the service locator.\n"
372
428
  )
429
+ return True
373
430
 
374
431
 
375
432
  def _scaffold_init(
@@ -436,14 +493,23 @@ def _scaffold_init(
436
493
  + " return {}\n"
437
494
  )
438
495
 
496
+ # Running the wizard again finds the file it wrote last time; asking
497
+ # whether to overwrite it with the very same text only invites a "no".
498
+ if os.path.isfile(init_path):
499
+ with open(init_path, "r", encoding="utf-8") as f:
500
+ if f.read().strip() == code.strip():
501
+ click.echo(click.style(" unchanged: ", dim=True) + "__init__.py")
502
+ return False
503
+
439
504
  # Warn before clobbering a developer-authored __init__.py
440
505
  if not _confirm_overwrite(init_path, "__init__.py"):
441
506
  click.echo(click.style(" skipped: ", dim=True) + "__init__.py (kept)")
442
- return
507
+ return False
443
508
 
444
509
  os.makedirs(os.path.dirname(init_path), exist_ok=True)
445
510
  with open(init_path, "w") as f:
446
511
  f.write(code)
512
+ return True
447
513
 
448
514
 
449
515
  def _update_env_py(
@@ -503,7 +569,9 @@ def _scaffold_hooks(
503
569
  for hook in fill_hooks:
504
570
  # notes.index.before_list → notes_index_before_list
505
571
  func_name = hook.replace(".", "_")
506
- func_lines.append(f"def {func_name}():")
572
+ # Slots pass their context (notes.index.note_extra passes the note),
573
+ # so a stub that takes nothing raised TypeError the first time it ran.
574
+ func_lines.append(f"def {func_name}(*args, **kwargs):")
507
575
  func_lines.append(f' """Fill the {hook} slot."""')
508
576
  func_lines.append(f' return render_template("hooks/{func_name}.html")')
509
577
  func_lines.append("")
@@ -516,22 +584,41 @@ def _scaffold_hooks(
516
584
  "\n\n" + "\n".join(func_lines) + "\n".join(register_lines) + "\n"
517
585
  )
518
586
 
519
- # Don't overwrite if hooks.py already has content
587
+ # Don't overwrite a hooks.py that already registers something. The
588
+ # scaffold only mentions register_template_hook inside its docstring, so a
589
+ # plain substring test mistook it for real content, skipped the write and
590
+ # still reported the hooks as scaffolded.
520
591
  if os.path.isfile(hooks_path):
521
592
  with open(hooks_path, "r") as f:
522
593
  existing = f.read()
523
- if "register_template_hook" in existing:
524
- return
594
+ if re.search(r"^register_template_hook\(", existing, re.MULTILINE):
595
+ click.echo(
596
+ click.style(" kept: ", dim=True)
597
+ + "hooks.py (already registers hooks)"
598
+ )
599
+ return False
525
600
 
526
601
  # Warn before clobbering developer-authored hooks.py
527
602
  if not _confirm_overwrite(hooks_path, "hooks.py"):
528
603
  click.echo(click.style(" skipped: ", dim=True) + "hooks.py (kept)")
529
- return
604
+ return False
530
605
 
531
606
  os.makedirs(os.path.dirname(hooks_path), exist_ok=True)
532
607
  with open(hooks_path, "w") as f:
533
608
  f.write(code)
534
609
 
610
+ # The stubs render hooks/<name>.html. Without those files every page
611
+ # holding one of the slots failed with TemplateNotFound until each stub
612
+ # was rewritten, so an empty template goes with each stub.
613
+ tpl_dir = os.path.join(os.path.dirname(hooks_path), "templates", "hooks")
614
+ os.makedirs(tpl_dir, exist_ok=True)
615
+ for hook in fill_hooks:
616
+ tpl = os.path.join(tpl_dir, f"{hook.replace('.', '_')}.html")
617
+ if not os.path.exists(tpl):
618
+ with open(tpl, "w", encoding="utf-8") as f:
619
+ f.write(f"{{# Fills the {hook} slot. #}}\n")
620
+ return True
621
+
535
622
 
536
623
  def _clean_scaffold_for_refinement(feature_path: str, ns: str, feature_name: str):
537
624
  """Clean up scaffold files that cause false positives in refinement features.
@@ -906,31 +993,36 @@ def feature_refinement(refiner_name):
906
993
  ns = d
907
994
  break
908
995
 
996
+ # Each helper says itself why it left a file alone; "scaffolded" is only
997
+ # claimed for what was actually written.
909
998
  for model, mixin in extend_models:
910
- _scaffold_mixin(refiner_path, ns, refiner_name, model, mixin)
911
- click.echo(click.style(" scaffolded: ", dim=True) + f"models.py <- {mixin}")
999
+ if _scaffold_mixin(refiner_path, ns, refiner_name, model, mixin):
1000
+ click.echo(
1001
+ click.style(" scaffolded: ", dim=True) + f"models.py <- {mixin}"
1002
+ )
912
1003
 
913
1004
  for target, replacement in override_services:
914
- _scaffold_service(refiner_path, ns, refiner_name, target, replacement)
915
- click.echo(
916
- click.style(" scaffolded: ", dim=True) + f"services.py <- {replacement}"
917
- )
1005
+ if _scaffold_service(refiner_path, ns, refiner_name, target, replacement):
1006
+ click.echo(
1007
+ click.style(" scaffolded: ", dim=True)
1008
+ + f"services.py <- {replacement}"
1009
+ )
918
1010
 
919
- _scaffold_init(
1011
+ if _scaffold_init(
920
1012
  refiner_path,
921
1013
  ns,
922
1014
  refiner_name,
923
1015
  base_short,
924
1016
  extend_models,
925
1017
  override_services,
926
- )
927
- click.echo(click.style(" scaffolded: ", dim=True) + "__init__.py")
1018
+ ):
1019
+ click.echo(click.style(" scaffolded: ", dim=True) + "__init__.py")
928
1020
 
929
- if fill_hooks:
930
- _scaffold_hooks(refiner_path, ns, refiner_name, fill_hooks)
1021
+ if fill_hooks and _scaffold_hooks(refiner_path, ns, refiner_name, fill_hooks):
931
1022
  click.echo(
932
1023
  click.style(" scaffolded: ", dim=True)
933
- + f"hooks.py <- {len(fill_hooks)} hook slot(s)"
1024
+ + f"hooks.py <- {len(fill_hooks)} hook slot(s), with empty "
1025
+ "templates/hooks/*.html"
934
1026
  )
935
1027
 
936
1028
  # Clean up scaffold files that are not needed for refinement features