py-wlcli 0.8.0__py3-none-any.whl

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 (320) hide show
  1. py_wlcli-0.8.0.dist-info/METADATA +139 -0
  2. py_wlcli-0.8.0.dist-info/RECORD +320 -0
  3. py_wlcli-0.8.0.dist-info/WHEEL +4 -0
  4. py_wlcli-0.8.0.dist-info/entry_points.txt +2 -0
  5. py_wlcli-0.8.0.dist-info/licenses/LICENSE +21 -0
  6. wl_cli/__init__.py +27 -0
  7. wl_cli/__main__.py +130 -0
  8. wl_cli/capabilities.py +79 -0
  9. wl_cli/cc/__init__.py +5 -0
  10. wl_cli/cc/checks.py +1019 -0
  11. wl_cli/cc/cli.py +370 -0
  12. wl_cli/cc/mutate.py +1350 -0
  13. wl_cli/cli_click.py +156 -0
  14. wl_cli/commands/__init__.py +84 -0
  15. wl_cli/commands/base.py +80 -0
  16. wl_cli/commands/buildcommands/__init__.py +1 -0
  17. wl_cli/commands/buildcommands/build.py +71 -0
  18. wl_cli/commands/buildcommands/build_utils.py +47 -0
  19. wl_cli/commands/buildcommands/builders/__init__.py +18 -0
  20. wl_cli/commands/buildcommands/builders/cleanup.py +35 -0
  21. wl_cli/commands/buildcommands/builders/coordinator.py +122 -0
  22. wl_cli/commands/buildcommands/builders/environment.py +81 -0
  23. wl_cli/commands/buildcommands/builders/rust_builder.py +39 -0
  24. wl_cli/commands/buildcommands/builders/stubs.py +58 -0
  25. wl_cli/commands/buildcommands/dist.py +326 -0
  26. wl_cli/commands/buildcommands/test.py +89 -0
  27. wl_cli/commands/clean/__init__.py +1 -0
  28. wl_cli/commands/clean/build.py +151 -0
  29. wl_cli/commands/clean/clean.py +294 -0
  30. wl_cli/commands/clean/clean_utils.py +77 -0
  31. wl_cli/commands/clean/core.py +236 -0
  32. wl_cli/commands/clean/dead_code/__init__.py +0 -0
  33. wl_cli/commands/clean/dead_code/dead_code_command.py +70 -0
  34. wl_cli/commands/clean/dead_code/detector.py +161 -0
  35. wl_cli/commands/clean/dead_code/fixer.py +176 -0
  36. wl_cli/commands/clean/dead_code/reporter.py +295 -0
  37. wl_cli/commands/clean/dead_code/utils.py +0 -0
  38. wl_cli/commands/clean/lfs.py +215 -0
  39. wl_cli/commands/clean/python.py +81 -0
  40. wl_cli/commands/clean/rust.py +91 -0
  41. wl_cli/commands/clean/utils/__init__.py +34 -0
  42. wl_cli/commands/clean/venv.py +91 -0
  43. wl_cli/commands/cleanup.py +73 -0
  44. wl_cli/commands/click_base.py +79 -0
  45. wl_cli/commands/config/__init__.py +1 -0
  46. wl_cli/commands/config/config.py +291 -0
  47. wl_cli/commands/debug/__init__.py +6 -0
  48. wl_cli/commands/debug/debug.py +309 -0
  49. wl_cli/commands/debug/debug_cache.py +86 -0
  50. wl_cli/commands/debug/debug_subagent.py +156 -0
  51. wl_cli/commands/debug/output_formatter.py +165 -0
  52. wl_cli/commands/debug/source_hasher.py +92 -0
  53. wl_cli/commands/format/__init__.py +13 -0
  54. wl_cli/commands/format/format_cache_adapter.py +125 -0
  55. wl_cli/commands/format/format_command.py +197 -0
  56. wl_cli/commands/format/format_coordinator.py +191 -0
  57. wl_cli/commands/format/format_executor.py +470 -0
  58. wl_cli/commands/format/format_outcome.py +44 -0
  59. wl_cli/commands/format/format_path_handler.py +1016 -0
  60. wl_cli/commands/format/format_progress.py +62 -0
  61. wl_cli/commands/format/format_reporter.py +31 -0
  62. wl_cli/commands/format/python_formatter.py +506 -0
  63. wl_cli/commands/format/rust_formatter.py +106 -0
  64. wl_cli/commands/gate/__init__.py +1 -0
  65. wl_cli/commands/gate/gate_command.py +862 -0
  66. wl_cli/commands/gate/preflight.py +445 -0
  67. wl_cli/commands/git/__init__.py +1 -0
  68. wl_cli/commands/git/config.py +169 -0
  69. wl_cli/commands/git/git_facts.py +60 -0
  70. wl_cli/commands/git/git_flow.py +86 -0
  71. wl_cli/commands/git/hook.py +112 -0
  72. wl_cli/commands/git/policy.py +216 -0
  73. wl_cli/commands/git/push_update.py +82 -0
  74. wl_cli/commands/initenv/__init__.py +1 -0
  75. wl_cli/commands/initenv/common/__init__.py +23 -0
  76. wl_cli/commands/initenv/common/check_git_bash.py +78 -0
  77. wl_cli/commands/initenv/common/error_handler.py +68 -0
  78. wl_cli/commands/initenv/common/exceptions.py +52 -0
  79. wl_cli/commands/initenv/components/__init__.py +4 -0
  80. wl_cli/commands/initenv/components/cargo_sync.py +240 -0
  81. wl_cli/commands/initenv/components/config_manager.py +106 -0
  82. wl_cli/commands/initenv/components/directory_utils.py +71 -0
  83. wl_cli/commands/initenv/components/git/__init__.py +9 -0
  84. wl_cli/commands/initenv/components/git/branch_creator.py +143 -0
  85. wl_cli/commands/initenv/components/git/git_flow_interface.py +24 -0
  86. wl_cli/commands/initenv/components/git/git_flow_manager.py +159 -0
  87. wl_cli/commands/initenv/components/git/git_lfs_manager.py +272 -0
  88. wl_cli/commands/initenv/components/git/git_utility.py +254 -0
  89. wl_cli/commands/initenv/components/git/gitignore_manager.py +53 -0
  90. wl_cli/commands/initenv/components/git_initializer.py +212 -0
  91. wl_cli/commands/initenv/components/i18n_manager.py +104 -0
  92. wl_cli/commands/initenv/components/log_manager.py +241 -0
  93. wl_cli/commands/initenv/components/project_structure/__init__.py +23 -0
  94. wl_cli/commands/initenv/components/project_structure/config_generator.py +360 -0
  95. wl_cli/commands/initenv/components/project_structure/directory_creator.py +184 -0
  96. wl_cli/commands/initenv/components/project_structure/hook_source.py +110 -0
  97. wl_cli/commands/initenv/components/project_structure/hooks_manager.py +995 -0
  98. wl_cli/commands/initenv/components/project_structure/readme_handler.py +56 -0
  99. wl_cli/commands/initenv/components/project_structure/rust_files_handler.py +30 -0
  100. wl_cli/commands/initenv/components/project_structure/setup_handler.py +103 -0
  101. wl_cli/commands/initenv/components/project_structure/sop_generator.py +73 -0
  102. wl_cli/commands/initenv/components/project_structure/template_copier.py +104 -0
  103. wl_cli/commands/initenv/components/project_structure/template_manager.py +195 -0
  104. wl_cli/commands/initenv/components/pyproject_generator.py +194 -0
  105. wl_cli/commands/initenv/components/rust_initializer.py +145 -0
  106. wl_cli/commands/initenv/components/symlink_cleaner.py +99 -0
  107. wl_cli/commands/initenv/components/symlink_manager.py +52 -0
  108. wl_cli/commands/initenv/components/venv_manager.py +118 -0
  109. wl_cli/commands/initenv/initenv.py +169 -0
  110. wl_cli/commands/initenv/services/__init__.py +11 -0
  111. wl_cli/commands/initenv/services/environment_initializer.py +288 -0
  112. wl_cli/commands/initenv/services/git_lfs_initializer.py +26 -0
  113. wl_cli/commands/initenv/services/project_structure_initializer.py +52 -0
  114. wl_cli/commands/lfs/README.md +221 -0
  115. wl_cli/commands/lfs/__init__.py +5 -0
  116. wl_cli/commands/lfs/cli.py +106 -0
  117. wl_cli/commands/lfs/config.py +164 -0
  118. wl_cli/commands/lfs/hook.py +61 -0
  119. wl_cli/commands/lfs/initializer.py +531 -0
  120. wl_cli/commands/lfs/manager.py +95 -0
  121. wl_cli/commands/lfs/policy.py +165 -0
  122. wl_cli/commands/lfs/watcher.py +294 -0
  123. wl_cli/commands/lint/__init__.py +1 -0
  124. wl_cli/commands/lint/lint_command.py +297 -0
  125. wl_cli/commands/lint/lint_executor.py +409 -0
  126. wl_cli/commands/lint/lint_reporter.py +579 -0
  127. wl_cli/commands/lint/lint_run.py +258 -0
  128. wl_cli/commands/lint/mypy_config.py +90 -0
  129. wl_cli/commands/registry.py +103 -0
  130. wl_cli/commands/self/__init__.py +7 -0
  131. wl_cli/commands/self/common/__init__.py +8 -0
  132. wl_cli/commands/self/common/capabilities.py +30 -0
  133. wl_cli/commands/self/common/config.py +27 -0
  134. wl_cli/commands/self/common/version_cache.py +85 -0
  135. wl_cli/commands/self/handlers/__init__.py +19 -0
  136. wl_cli/commands/self/handlers/base_handler.py +28 -0
  137. wl_cli/commands/self/handlers/install_handler.py +85 -0
  138. wl_cli/commands/self/handlers/status_handler.py +66 -0
  139. wl_cli/commands/self/handlers/uninstall_handler.py +58 -0
  140. wl_cli/commands/self/handlers/update_handler.py +102 -0
  141. wl_cli/commands/self/handlers/version_handler.py +16 -0
  142. wl_cli/commands/self/self_command.py +237 -0
  143. wl_cli/commands/self/services/__init__.py +15 -0
  144. wl_cli/commands/self/services/installation_manager.py +187 -0
  145. wl_cli/commands/self/services/local_installer.py +165 -0
  146. wl_cli/commands/self/services/pypi_updater.py +278 -0
  147. wl_cli/commands/self/services/windows_update.py +174 -0
  148. wl_cli/commands/testcommands/__init__.py +8 -0
  149. wl_cli/commands/testcommands/base_dependency_analyzer.py +198 -0
  150. wl_cli/commands/testcommands/batch_processor.py +278 -0
  151. wl_cli/commands/testcommands/cache_manager.py +1156 -0
  152. wl_cli/commands/testcommands/coverage_analyzer.py +955 -0
  153. wl_cli/commands/testcommands/coverage_cache_manager.py +183 -0
  154. wl_cli/commands/testcommands/progress_manager.py +79 -0
  155. wl_cli/commands/testcommands/python_env_detector.py +207 -0
  156. wl_cli/commands/testcommands/report_generator.py +101 -0
  157. wl_cli/commands/testcommands/single_test_executor.py +250 -0
  158. wl_cli/commands/testcommands/speed_checker.py +118 -0
  159. wl_cli/commands/testcommands/test_command_builder.py +165 -0
  160. wl_cli/commands/testcommands/test_commands.py +786 -0
  161. wl_cli/commands/testcommands/test_reporter.py +181 -0
  162. wl_cli/commands/testcommands/test_result_handler.py +96 -0
  163. wl_cli/commands/testcommands/test_runner.py +1343 -0
  164. wl_cli/commands/testcommands/test_utils.py +197 -0
  165. wl_cli/commands/testcommands/utils/__init__.py +17 -0
  166. wl_cli/commands/testcommands/utils/coverage_parser.py +98 -0
  167. wl_cli/commands/testcommands/utils/env_info.py +36 -0
  168. wl_cli/commands/testcommands/utils/junit_parser.py +75 -0
  169. wl_cli/commands/testcommands/utils/markdown_utils.py +20 -0
  170. wl_cli/commands/testcommands/utils/project_root.py +61 -0
  171. wl_cli/commands/update/__init__.py +9 -0
  172. wl_cli/commands/update/services/__init__.py +21 -0
  173. wl_cli/commands/update/services/backup_manager.py +207 -0
  174. wl_cli/commands/update/services/component_manager.py +83 -0
  175. wl_cli/commands/update/services/conflict_resolver.py +189 -0
  176. wl_cli/commands/update/services/vendors_updater.py +85 -0
  177. wl_cli/commands/update/services/version_tracker.py +127 -0
  178. wl_cli/commands/update/update_command.py +270 -0
  179. wl_cli/commands/update/utils/__init__.py +3 -0
  180. wl_cli/commands/version/__init__.py +9 -0
  181. wl_cli/commands/version/version_command.py +155 -0
  182. wl_cli/commands/workflow.py +477 -0
  183. wl_cli/env/__init__.py +5 -0
  184. wl_cli/env/cli.py +167 -0
  185. wl_cli/env_status.py +157 -0
  186. wl_cli/envelope.py +91 -0
  187. wl_cli/exceptions.py +85 -0
  188. wl_cli/lib/__init__.py +3 -0
  189. wl_cli/lib/rust_utils.py +35 -0
  190. wl_cli/mcp/__init__.py +6 -0
  191. wl_cli/mcp/capabilities.py +19 -0
  192. wl_cli/mcp/cli.py +22 -0
  193. wl_cli/mcp/gitlab.py +553 -0
  194. wl_cli/mcp/k8s.py +344 -0
  195. wl_cli/mcp/protocol.py +159 -0
  196. wl_cli/platform.py +139 -0
  197. wl_cli/quality/__init__.py +18 -0
  198. wl_cli/utils/__init__.py +7 -0
  199. wl_cli/utils/cache.py +491 -0
  200. wl_cli/utils/cache_ledger.py +355 -0
  201. wl_cli/utils/cache_store.py +546 -0
  202. wl_cli/utils/config/__init__.py +27 -0
  203. wl_cli/utils/config/config_loader.py +163 -0
  204. wl_cli/utils/config/core.py +862 -0
  205. wl_cli/utils/config/defaults.py +142 -0
  206. wl_cli/utils/config/env_exporter.py +86 -0
  207. wl_cli/utils/config/env_parser.py +108 -0
  208. wl_cli/utils/config/validation.py +280 -0
  209. wl_cli/utils/error_handler.py +165 -0
  210. wl_cli/utils/file_copier.py +56 -0
  211. wl_cli/utils/file_operations.py +506 -0
  212. wl_cli/utils/language_utils.py +122 -0
  213. wl_cli/utils/lazy_import.py +240 -0
  214. wl_cli/utils/log_levels.py +35 -0
  215. wl_cli/utils/log_rotators.py +74 -0
  216. wl_cli/utils/logging.py +458 -0
  217. wl_cli/utils/output_context.py +142 -0
  218. wl_cli/utils/performance/__init__.py +1 -0
  219. wl_cli/utils/performance/decorators.py +111 -0
  220. wl_cli/utils/performance/exporter.py +38 -0
  221. wl_cli/utils/performance/models.py +16 -0
  222. wl_cli/utils/performance/monitor.py +161 -0
  223. wl_cli/utils/performance_monitor.py +32 -0
  224. wl_cli/utils/platform_adapter.py +131 -0
  225. wl_cli/utils/pre_commit.py +266 -0
  226. wl_cli/utils/pre_commit_config.py +72 -0
  227. wl_cli/utils/root.py +521 -0
  228. wl_cli/utils/root_fallback.py +256 -0
  229. wl_cli/utils/run_evidence.py +236 -0
  230. wl_cli/utils/structured_logger.py +18 -0
  231. wl_cli/utils/structured_logging/__init__.py +19 -0
  232. wl_cli/utils/structured_logging/config.py +337 -0
  233. wl_cli/utils/structured_logging/core.py +177 -0
  234. wl_cli/utils/structured_logging/env.py +56 -0
  235. wl_cli/utils/structured_logging/filtering.py +75 -0
  236. wl_cli/utils/structured_logging/filters.py +55 -0
  237. wl_cli/utils/structured_logging/formatters.py +251 -0
  238. wl_cli/utils/structured_logging/handlers.py +308 -0
  239. wl_cli/utils/structured_logging/logger.py +354 -0
  240. wl_cli/utils/subprocess.py +71 -0
  241. wl_cli/utils/subprocess_utils.py +260 -0
  242. wl_cli/utils/test_utils/__init__.py +5 -0
  243. wl_cli/utils/test_utils/test_filter.py +36 -0
  244. wl_cli/utils/uv_run.py +79 -0
  245. wl_cli/utils/uv_tool.py +117 -0
  246. wl_cli/utils/uv_tree.py +71 -0
  247. wl_cli/utils/uv_workspace.py +174 -0
  248. wl_cli/utils/version/__init__.py +15 -0
  249. wl_cli/utils/version/pypi_version_checker.py +95 -0
  250. wl_cli/utils/version/version_comparator.py +133 -0
  251. wl_cli/utils/version/version_detectors.py +312 -0
  252. wl_cli/utils/version/version_service.py +131 -0
  253. wl_cli/utils/version/version_updater.py +198 -0
  254. wl_cli/utils/wl_dir_updater.py +1440 -0
  255. wl_cli/utils/workspace_detector/__init__.py +28 -0
  256. wl_cli/utils/workspace_detector/core.py +161 -0
  257. wl_cli/utils/workspace_detector/detection_strategies.py +107 -0
  258. wl_cli/utils/workspace_detector/exceptions.py +5 -0
  259. wl_cli/utils/workspace_detector/types.py +17 -0
  260. wl_cli/utils/workspace_detector/venv_resolver.py +84 -0
  261. wl_cli/vendors/config/.codespellrc +2 -0
  262. wl_cli/vendors/config/dependencies.toml +10 -0
  263. wl_cli/vendors/config/mypy.ini +62 -0
  264. wl_cli/vendors/config/pyproject_defaults.toml +28 -0
  265. wl_cli/vendors/config/template_config.toml +24 -0
  266. wl_cli/vendors/config/tool_configs.toml +76 -0
  267. wl_cli/vendors/git/.gitignore +122 -0
  268. wl_cli/vendors/git/.pre-commit-config.yaml +37 -0
  269. wl_cli/vendors/hooks/applypatch-msg.sample +15 -0
  270. wl_cli/vendors/hooks/commit-msg.sample +24 -0
  271. wl_cli/vendors/hooks/fsmonitor-watchman.sample +174 -0
  272. wl_cli/vendors/hooks/post-update.sample +8 -0
  273. wl_cli/vendors/hooks/pre-applypatch.sample +14 -0
  274. wl_cli/vendors/hooks/pre-commit +45 -0
  275. wl_cli/vendors/hooks/pre-commit.legacy +31 -0
  276. wl_cli/vendors/hooks/pre-commit.sample +49 -0
  277. wl_cli/vendors/hooks/pre-merge-commit.sample +13 -0
  278. wl_cli/vendors/hooks/pre-push.sample +60 -0
  279. wl_cli/vendors/hooks/pre-rebase.sample +169 -0
  280. wl_cli/vendors/hooks/pre-receive.sample +24 -0
  281. wl_cli/vendors/hooks/prepare-commit-msg.sample +42 -0
  282. wl_cli/vendors/hooks/push-to-checkout.sample +78 -0
  283. wl_cli/vendors/hooks/sendemail-validate.sample +77 -0
  284. wl_cli/vendors/hooks/unix/applypatch-msg.sample +15 -0
  285. wl_cli/vendors/hooks/unix/check_lockfile.py +58 -0
  286. wl_cli/vendors/hooks/unix/check_lockfile.sh +36 -0
  287. wl_cli/vendors/hooks/unix/commit-msg.sample +24 -0
  288. wl_cli/vendors/hooks/unix/fsmonitor-watchman.sample +174 -0
  289. wl_cli/vendors/hooks/unix/post-update.sample +8 -0
  290. wl_cli/vendors/hooks/unix/pre-applypatch.sample +14 -0
  291. wl_cli/vendors/hooks/unix/pre-commit +190 -0
  292. wl_cli/vendors/hooks/unix/pre-commit.legacy +31 -0
  293. wl_cli/vendors/hooks/unix/pre-commit.py +81 -0
  294. wl_cli/vendors/hooks/unix/pre-commit.sample +49 -0
  295. wl_cli/vendors/hooks/unix/pre-merge-commit.sample +13 -0
  296. wl_cli/vendors/hooks/unix/pre-push +417 -0
  297. wl_cli/vendors/hooks/unix/pre-push.sample +53 -0
  298. wl_cli/vendors/hooks/unix/pre-rebase.sample +169 -0
  299. wl_cli/vendors/hooks/unix/pre-receive.sample +24 -0
  300. wl_cli/vendors/hooks/unix/prepare-commit-msg.sample +42 -0
  301. wl_cli/vendors/hooks/update.sample +128 -0
  302. wl_cli/vendors/hooks/win/work.bat +63 -0
  303. wl_cli/vendors/project/.gitignore +90 -0
  304. wl_cli/vendors/project/.python-version +1 -0
  305. wl_cli/vendors/project/LICENSE +21 -0
  306. wl_cli/vendors/project/README.md +0 -0
  307. wl_cli/vendors/project/pyproject.toml +27 -0
  308. wl_cli/vendors/project/pyproject_no_workspace.toml +16 -0
  309. wl_cli/vendors/project/pyproject_workspace.toml +14 -0
  310. wl_cli/vendors/readme/README.md +120 -0
  311. wl_cli/vendors/rust/Cargo.toml +14 -0
  312. wl_cli/vendors/rust/lib/__init__.py +3 -0
  313. wl_cli/vendors/rust/lib/rust_utils.py +31 -0
  314. wl_cli/vendors/rust/src/lib.rs +13 -0
  315. wl_cli/vendors/rust/src/main.rs +3 -0
  316. wl_cli/vendors/rust/tests/test_rust_fallback.py +40 -0
  317. wl_cli/vendors/sop_doc/git-dev-sop.md +220 -0
  318. wl_cli/vendors/sop_doc/industrial-standard-sop.md +145 -0
  319. wl_cli/vendors/sop_doc/python-sop.md +342 -0
  320. wl_cli/vendors/sop_doc/rust-readme.md +219 -0
@@ -0,0 +1,56 @@
1
+ """README template handler."""
2
+
3
+ from .....utils.logging import log_debug_pair, log_info_pair
4
+ from ..config_manager import ConfigManager
5
+
6
+
7
+ class ReadmeHandler:
8
+ """Handle README template processing."""
9
+
10
+ def __init__(self):
11
+ self.config_manager = ConfigManager()
12
+
13
+ def create_readme(self, project_name: str) -> None:
14
+ """Copy README template and customize it for the project."""
15
+ try:
16
+ # Calculate path to vendors/readme/README.md relative to this file
17
+ template_path = self.config_manager.get_vendor_config_path(
18
+ "readme/README.md"
19
+ )
20
+
21
+ if template_path.exists():
22
+ # Read the template
23
+ with open(template_path, encoding="utf-8") as f:
24
+ template_content = f.read()
25
+
26
+ # Customize the template
27
+ customized_content = template_content.format(
28
+ project_name=project_name,
29
+ project_description=f"{project_name} - A Python project",
30
+ cli_command="wl", # Default CLI command
31
+ )
32
+
33
+ # Write to project README.md
34
+ with open("README.md", "w", encoding="utf-8") as f:
35
+ f.write(customized_content)
36
+
37
+ log_debug_pair(
38
+ f"✓ README.md template copied and customized for: {project_name}",
39
+ f"✓ README.md 模板复制并定制化完成: {project_name}",
40
+ )
41
+ else:
42
+ # Fallback to simple README if template doesn't exist
43
+ with open("README.md", "w", encoding="utf-8") as f:
44
+ f.write(f"# {project_name}\n")
45
+ log_debug_pair(
46
+ f"✓ Created simple README.md with project name: {project_name}",
47
+ f"✓ 创建简单 README.md,项目名: {project_name}",
48
+ )
49
+ except Exception as e:
50
+ # Fallback to simple README if customization fails
51
+ with open("README.md", "w", encoding="utf-8") as f:
52
+ f.write(f"# {project_name}\n")
53
+ log_info_pair(
54
+ f"Warning: Failed to customize README.md, created simple one: {e}",
55
+ f"警告: 定制 README.md 失败,创建简单版本: {e}",
56
+ )
@@ -0,0 +1,30 @@
1
+ """Rust example files handler."""
2
+
3
+ from pathlib import Path
4
+
5
+ from .template_copier import TemplateCopier
6
+
7
+
8
+ class RustFilesHandler(TemplateCopier):
9
+ """Copy Rust example files from vendors to project using config-driven approach."""
10
+
11
+ CONFIG_SECTION = "rust_templates"
12
+
13
+ def copy_rust_files(self, project_name: str, dest_base: Path | None = None) -> None:
14
+ """Copy Rust example files from vendors to project.
15
+
16
+ Args:
17
+ project_name: Normalized project/package name used in dest templates.
18
+ dest_base: Optional base directory (``packages/<normalized>`` in
19
+ workspace mode) prefixed to every configured destination.
20
+ """
21
+ self._copy_from_section(
22
+ project_name,
23
+ action_name_en="copy Rust example files",
24
+ action_name_zh="复制 Rust 示例文件",
25
+ success_msg_en="✓ Copied {name} to {dest.parent}",
26
+ success_msg_zh="✓ 复制 {name} 到 {dest.parent}",
27
+ fail_msg="Warning: Failed to copy {name}: {message}",
28
+ fail_msg_zh="警告: 复制 {name} 失败: {message}",
29
+ dest_base=dest_base,
30
+ )
@@ -0,0 +1,103 @@
1
+ """Project structure setup handler.
2
+
3
+ This module serves as the entry point for the modularized project structure setup functionality.
4
+ 主要功能委托给专门的模块以获得更好的组织结构。
5
+ """
6
+
7
+ from pathlib import Path
8
+
9
+ from .....utils.root_fallback import current_context
10
+ from ..config_manager import ConfigManager
11
+ from .config_generator import ConfigGenerator
12
+ from .directory_creator import DirectoryCreator, normalize_project_name
13
+ from .hooks_manager import sync_hooks
14
+ from .readme_handler import ReadmeHandler
15
+ from .rust_files_handler import RustFilesHandler
16
+ from .sop_generator import SopGenerator
17
+ from .template_manager import TemplateManager
18
+
19
+
20
+ class ProjectStructureSetup:
21
+ """Project structure setup handler - now a coordinator for specialized modules."""
22
+
23
+ def __init__(self) -> None:
24
+ self.config_manager = ConfigManager()
25
+ self.directory_creator = DirectoryCreator()
26
+ self.readme_handler = ReadmeHandler()
27
+ self.config_generator = ConfigGenerator()
28
+ self.sop_generator = SopGenerator()
29
+ self.template_manager = TemplateManager()
30
+ self.rust_files_handler = RustFilesHandler()
31
+
32
+ def setup(
33
+ self,
34
+ project_name: str,
35
+ use_workspace: bool = True,
36
+ copy_pyproject: bool = True,
37
+ workspace_mode: bool = False,
38
+ ) -> None:
39
+ """Setup project structure by coordinating specialized modules.
40
+
41
+ Args:
42
+ project_name: Name of the project
43
+ use_workspace: If True, use pyproject.toml with workspace config.
44
+ If False, use pyproject.toml without workspace config.
45
+ copy_pyproject: If True, copy pyproject.toml from template.
46
+ If False, skip pyproject.toml (will be generated by PyProjectGenerator).
47
+ workspace_mode: If True, create packages/{name}/ layout instead of src/{name}/.
48
+ """
49
+ # The `wl init` entry resolves the roots **once** here and hands the
50
+ # deploy root to the two `.wl` steps below (#160/F5). Each of them is a
51
+ # public entry that can also be called directly (and then resolves the
52
+ # one root it needs itself), but on this path they must not each walk
53
+ # the CWD again -- that would resolve the same value twice. Resolving at
54
+ # entry rather than after the file creation below is safe: both roots
55
+ # are "git top, else marker, else start", and none of the steps can move
56
+ # the git top.
57
+ context = current_context()
58
+
59
+ # Normalize the project name exactly once (#175): the same value feeds
60
+ # directory creation, template rendering and both config-driven
61
+ # copiers, so a hyphenated name can never create a parallel
62
+ # ``src/my-project/`` tree next to ``src/my_project/``.
63
+ normalized_name = normalize_project_name(project_name)
64
+ package_base = Path("packages") / normalized_name if workspace_mode else None
65
+
66
+ # Create main project structure
67
+ self.directory_creator.create_structure(
68
+ normalized_name, workspace_mode=workspace_mode
69
+ )
70
+
71
+ # Copy project template files including .coveragerc
72
+ self.template_manager.copy_template_files(
73
+ use_workspace=use_workspace,
74
+ copy_pyproject=copy_pyproject,
75
+ workspace_mode=workspace_mode,
76
+ project_name=normalized_name,
77
+ )
78
+
79
+ # Create README.md if it doesn't exist or is empty
80
+ readme_file = Path("README.md")
81
+ should_create_readme = (
82
+ not readme_file.exists()
83
+ or readme_file.read_text(encoding="utf-8").strip() == ""
84
+ )
85
+ if should_create_readme:
86
+ self.readme_handler.create_readme(project_name)
87
+
88
+ # Generate SOP files for Rust-Python integration. In workspace mode the
89
+ # package-scoped templates share the single ``packages/<norm>`` base.
90
+ self.sop_generator.generate_sop_files(normalized_name, dest_base=package_base)
91
+
92
+ # Copy Rust example files
93
+ self.rust_files_handler.copy_rust_files(normalized_name, dest_base=package_base)
94
+
95
+ # Generate configuration files in .wl directory
96
+ self.config_generator.generate_configs(context.deploy)
97
+
98
+ # Sync git hooks explicitly (generate_configs is pure template migration)
99
+ sync_hooks(context.deploy)
100
+
101
+ from .....utils.logging import log_info_pair
102
+
103
+ log_info_pair("✓ Project structure created successfully", "✓ 项目结构创建成功")
@@ -0,0 +1,73 @@
1
+ """SOP file generator."""
2
+
3
+ from pathlib import Path
4
+
5
+ from .template_copier import TemplateCopier
6
+
7
+
8
+ class SopGenerator(TemplateCopier):
9
+ """Generate SOP files for Rust-Python integration using config-driven approach."""
10
+
11
+ CONFIG_SECTION = "sop_templates"
12
+
13
+ def generate_sop_files(
14
+ self, project_name: str, dest_base: Path | None = None
15
+ ) -> None:
16
+ """Generate SOP files for Rust-Python integration.
17
+
18
+ Args:
19
+ project_name: Normalized project/package name used in dest templates.
20
+ dest_base: Optional base directory (``packages/<normalized>`` in
21
+ workspace mode) prefixed to every configured destination.
22
+ """
23
+ existed = self._existing_sop_dests(project_name, dest_base)
24
+ self._copy_from_section(
25
+ project_name,
26
+ action_name_en="generate SOP files",
27
+ action_name_zh="生成 SOP 文件",
28
+ success_msg_en="✓ Generated SOP file: {dest}",
29
+ success_msg_zh="✓ 生成 SOP 文件: {dest}",
30
+ fail_msg="Warning: Failed to generate SOP {name}: {message}",
31
+ fail_msg_zh="警告: 生成 SOP {name} 失败: {message}",
32
+ dest_base=dest_base,
33
+ )
34
+ self._rewrite_self_references(project_name, existed, dest_base)
35
+
36
+ def _sop_destinations(
37
+ self, project_name: str, dest_base: Path | None = None
38
+ ) -> list[Path]:
39
+ """Resolve the configured SOP destinations for this project name."""
40
+ base = dest_base if dest_base is not None else Path()
41
+ dests: list[Path] = []
42
+ for template in self.config.get(self.CONFIG_SECTION, {}).values():
43
+ dests.append(base / template["dest"].format(project_name=project_name))
44
+ return dests
45
+
46
+ def _existing_sop_dests(
47
+ self, project_name: str, dest_base: Path | None = None
48
+ ) -> set[Path]:
49
+ return {
50
+ d for d in self._sop_destinations(project_name, dest_base) if d.exists()
51
+ }
52
+
53
+ def _rewrite_self_references(
54
+ self, project_name: str, existed: set[Path], dest_base: Path | None = None
55
+ ) -> None:
56
+ """Rewrite freshly generated SOP copies so they no longer self-reference
57
+ wl_cli (industrial-SOP D1 / map #49 #52).
58
+
59
+ The vendored SOP templates are repo-agnostic prose, but keep the
60
+ ``wl_cli`` tokens as runnable examples. We copy them verbatim
61
+ (template=false, since the markdown's code fences contain literal
62
+ braces that ``.format`` would break on) and then replace the token with
63
+ the generated project's own name, so the SOP a new project receives
64
+ matches its real package/module (``{project_name}_native`` etc.).
65
+ """
66
+ for dest in self._sop_destinations(project_name, dest_base):
67
+ if dest.exists() and dest not in existed:
68
+ text = dest.read_text(encoding="utf-8")
69
+ if "wl_cli" in text:
70
+ dest.write_text(
71
+ text.replace("wl_cli", project_name),
72
+ encoding="utf-8",
73
+ )
@@ -0,0 +1,104 @@
1
+ """Shared template copying implementation for config-driven project generators.
2
+
3
+ Both SopGenerator and RustFilesHandler copy vendored template files into the
4
+ project based on sections in template_config.toml. This base class factors out
5
+ their identical initialization, config loading, and copy loop so changes to
6
+ copying behaviour (error handling, template placeholder support, logging) are
7
+ made in one place.
8
+ """
9
+
10
+ from pathlib import Path
11
+
12
+ import tomllib
13
+
14
+ from .....utils.file_copier import FileCopier
15
+ from .....utils.logging import log_debug_pair, log_info_pair
16
+ from ..config_manager import ConfigManager
17
+
18
+
19
+ class TemplateCopier:
20
+ """Copy vendored template files based on a template_config.toml section."""
21
+
22
+ # Subclasses set the config section key, e.g. "sop_templates".
23
+ CONFIG_SECTION = ""
24
+
25
+ def __init__(self) -> None:
26
+ """Initialize with a config manager, file copier, and loaded config."""
27
+ self.config_manager = ConfigManager()
28
+ self.file_copier = FileCopier()
29
+ self.config: dict = {}
30
+ self._load_config()
31
+
32
+ def _load_config(self) -> None:
33
+ """Load template_config.toml, falling back to an empty dict on failure."""
34
+ config_path = self.config_manager.get_vendor_config_path(
35
+ "config/template_config.toml"
36
+ )
37
+ if config_path.exists():
38
+ try:
39
+ with open(config_path, "rb") as f:
40
+ self.config = tomllib.load(f)
41
+ except Exception as e:
42
+ log_info_pair(
43
+ f"Warning: Failed to load template config: {e}",
44
+ f"警告: 加载模板配置失败: {e}",
45
+ )
46
+ self.config = {}
47
+ else:
48
+ self.config = {}
49
+
50
+ def _copy_from_section(
51
+ self,
52
+ project_name: str,
53
+ *,
54
+ action_name_en: str,
55
+ action_name_zh: str,
56
+ success_msg_en: str,
57
+ success_msg_zh: str,
58
+ fail_msg: str,
59
+ fail_msg_zh: str,
60
+ dest_base: Path | None = None,
61
+ ) -> None:
62
+ """Copy files described by CONFIG_SECTION, skipping existing targets.
63
+
64
+ Args:
65
+ project_name: Name of the project, used for {project_name} placeholders.
66
+ action_name_en: English action label used in the top-level failure message.
67
+ action_name_zh: Chinese action label used in the top-level failure message.
68
+ success_msg_en: English success message template with {dest} or {name}.
69
+ success_msg_zh: Chinese success message template with {dest} or {name}.
70
+ fail_msg: English failure message template with {name} and {message}.
71
+ fail_msg_zh: Chinese failure message template with {name} and {message}.
72
+ dest_base: Optional base directory prefixed to every configured dest.
73
+ In workspace mode the caller passes ``packages/<normalized>`` so
74
+ package-scoped templates land inside the sub-package (#175).
75
+ """
76
+ try:
77
+ section_config = self.config.get(self.CONFIG_SECTION, {})
78
+ context = {"project_name": project_name}
79
+ base = dest_base if dest_base is not None else Path()
80
+
81
+ for name, template in section_config.items():
82
+ src = self.config_manager.get_vendor_config_path(template["src"])
83
+ dest = base / template["dest"].format(**context)
84
+ is_template = template.get("template", False)
85
+
86
+ if not dest.exists():
87
+ result = self.file_copier.copy_file(
88
+ src, dest, template=is_template, context=context
89
+ )
90
+ if result.success:
91
+ log_debug_pair(
92
+ success_msg_en.format(dest=dest, name=name),
93
+ success_msg_zh.format(dest=dest, name=name),
94
+ )
95
+ else:
96
+ log_info_pair(
97
+ fail_msg.format(name=name, message=result.message),
98
+ fail_msg_zh.format(name=name, message=result.message),
99
+ )
100
+ except Exception as e:
101
+ log_info_pair(
102
+ f"Warning: Failed to {action_name_en}: {e}",
103
+ f"警告: {action_name_zh}失败: {e}",
104
+ )
@@ -0,0 +1,195 @@
1
+ """Project template manager."""
2
+
3
+ from pathlib import Path
4
+
5
+ import tomllib
6
+
7
+ from .....utils.file_copier import FileCopier
8
+ from .....utils.logging import log_debug, log_debug_pair, log_info, log_info_pair
9
+ from .....utils.root import RootKind
10
+ from .....utils.root_fallback import current_root
11
+ from ..config_manager import ConfigManager
12
+ from .directory_creator import normalize_project_name
13
+
14
+
15
+ class TemplateManager:
16
+ """Copy project template files using config-driven approach."""
17
+
18
+ def __init__(self):
19
+ self.config_manager = ConfigManager()
20
+ self.file_copier = FileCopier()
21
+ self.config = {}
22
+ self._load_config()
23
+
24
+ def _load_config(self):
25
+ config_path = self.config_manager.get_vendor_config_path(
26
+ "config/template_config.toml"
27
+ )
28
+ if config_path.exists():
29
+ try:
30
+ with open(config_path, "rb") as f:
31
+ self.config = tomllib.load(f)
32
+ except Exception as e:
33
+ log_info_pair(
34
+ f"Warning: Failed to load template config: {e}",
35
+ f"警告: 加载模板配置失败: {e}",
36
+ )
37
+ self.config = {}
38
+ else:
39
+ self.config = {}
40
+
41
+ def _validate_template_files(self) -> list[str]:
42
+ missing_files = []
43
+ all_templates = {}
44
+ all_templates.update(self.config.get("core_templates", {}))
45
+ all_templates.update(self.config.get("rust_templates", {}))
46
+ all_templates.update(self.config.get("sop_templates", {}))
47
+ all_templates.update(self.config.get("config_templates", {}))
48
+
49
+ for name, template in all_templates.items():
50
+ src = self.config_manager.get_vendor_config_path(template["src"])
51
+ if not src.exists():
52
+ missing_files.append(f"{name}: {template['src']}")
53
+
54
+ return missing_files
55
+
56
+ def copy_template_files(
57
+ self,
58
+ use_workspace: bool = True,
59
+ copy_pyproject: bool = True,
60
+ workspace_mode: bool = False,
61
+ project_name: str | None = None,
62
+ ) -> None:
63
+ """Copy project template files (core + config templates).
64
+
65
+ Args:
66
+ use_workspace: If True, use pyproject.toml with workspace config.
67
+ If False, use pyproject.toml without workspace config.
68
+ copy_pyproject: If True, copy pyproject.toml from template.
69
+ If False, skip pyproject.toml (will be generated by PyProjectGenerator).
70
+ workspace_mode: If True, copy workspace layout templates.
71
+ project_name: Normalized project name resolved once by the caller
72
+ (#175). Falls back to the CWD basename for direct calls.
73
+ """
74
+ missing = self._validate_template_files()
75
+ if missing:
76
+ log_info_pair(
77
+ f"Warning: Missing template files: {missing}",
78
+ f"警告: 缺失模板文件: {missing}",
79
+ )
80
+
81
+ try:
82
+ self._copy_core_templates()
83
+ self._copy_config_templates()
84
+
85
+ if copy_pyproject:
86
+ if workspace_mode:
87
+ self._copy_workspace_templates(project_name)
88
+ else:
89
+ self._copy_pyproject(use_workspace)
90
+
91
+ except Exception as e:
92
+ log_info_pair(
93
+ f"Warning: Failed to copy project template files: {e}",
94
+ f"警告: 复制项目模板文件失败: {e}",
95
+ )
96
+
97
+ def _copy_core_templates(self):
98
+ core_config = self.config.get("core_templates", {})
99
+ for name, template in core_config.items():
100
+ src = self.config_manager.get_vendor_config_path(template["src"])
101
+ dest = Path(template["dest"])
102
+
103
+ if not dest.exists():
104
+ result = self.file_copier.copy_file(src, dest)
105
+ if result.success:
106
+ log_debug_pair(
107
+ f"✓ Copied {name} from template", f"✓ 从模板复制 {name}"
108
+ )
109
+ else:
110
+ log_info(f"Warning: Failed to copy {name}: {result.message}")
111
+
112
+ def _copy_config_templates(self):
113
+ """Copy the ``.wl`` config templates, anchored at the deploy root.
114
+
115
+ These destinations all live under ``.wl`` (``.wl/mypy.ini`` and friends),
116
+ which since #95 / #96 belongs inside the git work tree -- the same base
117
+ :class:`ConfigGenerator` uses. Resolving them against the CWD instead
118
+ dropped a second, partial ``.wl`` into an ``wl init project`` run from a
119
+ subdirectory (issue #95 / #96 fix-up, F1).
120
+ """
121
+ # The same deploy root :class:`ConfigGenerator` uses (#160): the one
122
+ # fallback policy, taken as a bare Path to join the template paths onto.
123
+ deploy_root = current_root(RootKind.DEPLOY).path
124
+ config_config = self.config.get("config_templates", {})
125
+ for name, template in config_config.items():
126
+ src = self.config_manager.get_vendor_config_path(template["src"])
127
+ dest = deploy_root / template["dest"]
128
+
129
+ if not dest.exists():
130
+ dest.parent.mkdir(parents=True, exist_ok=True)
131
+ result = self.file_copier.copy_file(src, dest)
132
+ if result.success:
133
+ log_debug_pair(
134
+ f"✓ Copied {name} from template", f"✓ 从模板复制 {name}"
135
+ )
136
+ else:
137
+ log_info(f"Warning: Failed to copy {name}: {result.message}")
138
+
139
+ def _copy_workspace_templates(self, project_name: str | None = None) -> None:
140
+ """Copy workspace layout templates (root pyproject + package pyproject)."""
141
+ pyproject_config = self.config.get("pyproject_templates", {})
142
+ normalized_name = normalize_project_name(project_name or Path.cwd().name)
143
+
144
+ # Copy workspace root pyproject.toml
145
+ root_key = "workspace_root"
146
+ if root_key in pyproject_config:
147
+ template = pyproject_config[root_key]
148
+ src = self.config_manager.get_vendor_config_path(template["src"])
149
+ dest = Path(template["dest"])
150
+ if not dest.exists() and src.exists():
151
+ result = self.file_copier.copy_file(src, dest)
152
+ if result.success:
153
+ log_debug("✓ Copied workspace root pyproject.toml")
154
+ else:
155
+ log_info(
156
+ f"Warning: Failed to copy workspace pyproject.toml: {result.message}"
157
+ )
158
+
159
+ # Copy package pyproject.toml
160
+ pkg_key = "workspace_package"
161
+ if pkg_key in pyproject_config:
162
+ template = pyproject_config[pkg_key]
163
+ src = self.config_manager.get_vendor_config_path(template["src"])
164
+ dest_str = template["dest"].replace("{project_name}", normalized_name)
165
+ dest = Path(dest_str)
166
+ dest.parent.mkdir(parents=True, exist_ok=True)
167
+ if not dest.exists() and src.exists():
168
+ result = self.file_copier.copy_file(src, dest)
169
+ if result.success:
170
+ log_debug(f"✓ Copied package pyproject.toml to {dest}")
171
+ else:
172
+ log_info(
173
+ f"Warning: Failed to copy package pyproject.toml: {result.message}"
174
+ )
175
+
176
+ def _copy_pyproject(self, use_workspace: bool):
177
+ pyproject_config = self.config.get("pyproject_templates", {})
178
+ key = "workspace" if use_workspace else "no_workspace"
179
+
180
+ if key in pyproject_config:
181
+ template = pyproject_config[key]
182
+ src = self.config_manager.get_vendor_config_path(template["src"])
183
+ dest = Path(template["dest"])
184
+
185
+ if not dest.exists() and src.exists():
186
+ result = self.file_copier.copy_file(src, dest)
187
+ if result.success:
188
+ log_debug_pair(
189
+ "✓ Copied pyproject.toml from template",
190
+ "✓ 从模板复制 pyproject.toml",
191
+ )
192
+ else:
193
+ log_info(
194
+ f"Warning: Failed to copy pyproject.toml: {result.message}"
195
+ )