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,295 @@
1
+ """Dead code reporter module.
2
+ Handles reporting for dead code cleanup command.
3
+ """
4
+
5
+ import json
6
+ from dataclasses import dataclass
7
+ from typing import Any
8
+
9
+
10
+ @dataclass
11
+ class DeadCodeIssue:
12
+ """Data structure for a dead code issue."""
13
+
14
+ type: str
15
+ file: str
16
+ line: int | None
17
+ name: str | None
18
+ severity: str
19
+ fixed: bool
20
+
21
+
22
+ @dataclass
23
+ class TestSuggestion:
24
+ """Data structure for a test suggestion."""
25
+
26
+ function: str | None
27
+ file: str
28
+ line: int | None
29
+ suggestion: str
30
+ suggestion_zh: str
31
+
32
+
33
+ @dataclass
34
+ class ReportSummary:
35
+ """Data structure for the report summary."""
36
+
37
+ total_issues: int
38
+ auto_fixed: int
39
+ manual_required: int
40
+ test_suggestions: int
41
+
42
+
43
+ class DeadCodeReporter:
44
+ """Reporter for dead code cleanup command."""
45
+
46
+ def __init__(self) -> None:
47
+ """Initialize the reporter.
48
+
49
+ Language is not a constructor input: every message goes through
50
+ ``log_*_pair``, which picks the variant itself (#167).
51
+ """
52
+ self._import_logger()
53
+
54
+ def _import_logger(self) -> None:
55
+ """Lazy import the logging functions."""
56
+ from wl_cli.utils.logging import (
57
+ log_info_pair,
58
+ log_warning_pair,
59
+ )
60
+
61
+ self.log_info_pair = log_info_pair
62
+ self.log_warning_pair = log_warning_pair
63
+
64
+ def report_cli(
65
+ self,
66
+ issues: list[DeadCodeIssue],
67
+ test_suggestions: list[TestSuggestion],
68
+ dry_run: bool = False,
69
+ ) -> None:
70
+ """Generate CLI output report.
71
+
72
+ Args:
73
+ issues: List of dead code issues.
74
+ test_suggestions: List of test suggestions.
75
+ dry_run: Whether this is a dry run.
76
+ """
77
+ summary = self._calculate_summary(issues, test_suggestions)
78
+ self._report_scanning()
79
+ self._report_total_issues(summary)
80
+ self._report_auto_fixed(issues)
81
+ if dry_run:
82
+ self._report_dry_run_issues(issues)
83
+ self._report_manual_required(issues)
84
+ self._report_test_suggestions(test_suggestions)
85
+ self._report_completion(summary)
86
+
87
+ def _report_dry_run_issues(self, issues: list[DeadCodeIssue]) -> None:
88
+ """Report issues that would be fixed in dry run mode.
89
+
90
+ Args:
91
+ issues: List of dead code issues.
92
+ """
93
+ dry_run_issues = [issue for issue in issues if issue.severity == "safe"]
94
+ if dry_run_issues:
95
+ self.log_info_pair(
96
+ "Would fix (safe operations - dry run):",
97
+ "将修复(安全操作 - 试运行):",
98
+ )
99
+ for issue in dry_run_issues:
100
+ self._report_single_issue(issue)
101
+
102
+ def report_json(
103
+ self,
104
+ issues: list[DeadCodeIssue],
105
+ test_suggestions: list[TestSuggestion],
106
+ ) -> str:
107
+ """Generate JSON output report.
108
+
109
+ Args:
110
+ issues: List of dead code issues.
111
+ test_suggestions: List of test suggestions.
112
+
113
+ Returns:
114
+ JSON string of the report.
115
+ """
116
+ summary = self._calculate_summary(issues, test_suggestions)
117
+
118
+ json_data = {
119
+ "summary": {
120
+ "total_issues": summary.total_issues,
121
+ "auto_fixed": summary.auto_fixed,
122
+ "manual_required": summary.manual_required,
123
+ "test_suggestions": summary.test_suggestions,
124
+ },
125
+ "issues": [self._issue_to_dict(issue) for issue in issues],
126
+ "test_suggestions": [
127
+ self._suggestion_to_dict(suggestion) for suggestion in test_suggestions
128
+ ],
129
+ }
130
+
131
+ return json.dumps(json_data, ensure_ascii=False, indent=2)
132
+
133
+ def _calculate_summary(
134
+ self,
135
+ issues: list[DeadCodeIssue],
136
+ test_suggestions: list[TestSuggestion],
137
+ ) -> ReportSummary:
138
+ """Calculate the report summary.
139
+
140
+ Args:
141
+ issues: List of dead code issues.
142
+ test_suggestions: List of test suggestions.
143
+
144
+ Returns:
145
+ ReportSummary object.
146
+ """
147
+ total_issues = len(issues)
148
+ auto_fixed = sum(1 for issue in issues if issue.fixed)
149
+ manual_required = sum(
150
+ 1 for issue in issues if not issue.fixed and issue.severity != "safe"
151
+ )
152
+
153
+ return ReportSummary(
154
+ total_issues=total_issues,
155
+ auto_fixed=auto_fixed,
156
+ manual_required=manual_required,
157
+ test_suggestions=len(test_suggestions),
158
+ )
159
+
160
+ def _report_scanning(self) -> None:
161
+ """Report that scanning is in progress."""
162
+ self.log_info_pair("Scanning for dead code...", "正在扫描死代码...")
163
+
164
+ def _report_total_issues(self, summary: ReportSummary) -> None:
165
+ """Report the total number of issues found.
166
+
167
+ Args:
168
+ summary: Report summary.
169
+ """
170
+ if summary.total_issues > 0:
171
+ self.log_info_pair(
172
+ f"Found {summary.total_issues} issues.",
173
+ f"发现 {summary.total_issues} 个问题。",
174
+ )
175
+ else:
176
+ self.log_info_pair("No dead code issues found.", "未发现死代码问题。")
177
+
178
+ def _report_auto_fixed(self, issues: list[DeadCodeIssue]) -> None:
179
+ """Report the auto-fixed issues.
180
+
181
+ Args:
182
+ issues: List of dead code issues.
183
+ """
184
+ auto_fixed_issues = [issue for issue in issues if issue.fixed]
185
+ if auto_fixed_issues:
186
+ self.log_info_pair(
187
+ "Auto-fixed (safe operations):", "自动修复(安全操作):"
188
+ )
189
+ for issue in auto_fixed_issues:
190
+ self._report_single_issue(issue)
191
+
192
+ def _report_manual_required(self, issues: list[DeadCodeIssue]) -> None:
193
+ """Report the issues that require manual confirmation.
194
+
195
+ Args:
196
+ issues: List of dead code issues.
197
+ """
198
+ manual_issues = [
199
+ issue for issue in issues if not issue.fixed and issue.severity != "safe"
200
+ ]
201
+ if manual_issues:
202
+ self.log_warning_pair("Requires manual review:", "需要手动确认:")
203
+ for issue in manual_issues:
204
+ self._report_single_issue(issue)
205
+
206
+ def _report_single_issue(self, issue: DeadCodeIssue) -> None:
207
+ """Report a single issue.
208
+
209
+ Args:
210
+ issue: Dead code issue.
211
+ """
212
+ if issue.type == "unused_import":
213
+ self.log_info_pair(
214
+ f" - Removed unused import: {issue.name} ({issue.file}:{issue.line})",
215
+ f" - 移除未使用的导入: {issue.name} ({issue.file}:{issue.line})",
216
+ )
217
+ elif issue.type == "unused_variable":
218
+ self.log_info_pair(
219
+ f" - Removed unused variable: {issue.name} ({issue.file}:{issue.line})",
220
+ f" - 移除未使用的变量: {issue.name} ({issue.file}:{issue.line})",
221
+ )
222
+ elif issue.type == "possibly_abandoned_module":
223
+ self.log_warning_pair(
224
+ f" - Possibly abandoned module: {issue.file} (0 importers)",
225
+ f" - 可能是废弃模块: {issue.file} (0 个导入者)",
226
+ )
227
+ elif issue.type == "no_test_coverage":
228
+ self.log_warning_pair(
229
+ f" - Function without test coverage: {issue.name} ({issue.file}:{issue.line})",
230
+ f" - 无测试覆盖的函数: {issue.name} ({issue.file}:{issue.line})",
231
+ )
232
+ else:
233
+ self.log_info_pair(
234
+ f" - {issue.type}: {issue.name} ({issue.file}:{issue.line})",
235
+ f" - {issue.type}: {issue.name} ({issue.file}:{issue.line})",
236
+ )
237
+
238
+ def _report_test_suggestions(self, test_suggestions: list[TestSuggestion]) -> None:
239
+ """Report test suggestions.
240
+
241
+ Args:
242
+ test_suggestions: List of test suggestions.
243
+ """
244
+ if test_suggestions:
245
+ self.log_info_pair("Test suggestions:", "测试建议:")
246
+ for suggestion in test_suggestions:
247
+ self.log_info_pair(
248
+ f" - {suggestion.suggestion}", f" - {suggestion.suggestion_zh}"
249
+ )
250
+
251
+ def _report_completion(self, summary: ReportSummary) -> None:
252
+ """Report scan completion.
253
+
254
+ Args:
255
+ summary: Report summary.
256
+ """
257
+ self.log_info_pair(
258
+ f"Scan complete: {summary.auto_fixed} fixed, {summary.manual_required} manual required",
259
+ f"扫描完成:修复 {summary.auto_fixed} 个,待手动处理 {summary.manual_required} 个",
260
+ )
261
+
262
+ def _issue_to_dict(self, issue: DeadCodeIssue) -> dict[str, Any]:
263
+ """Convert a DeadCodeIssue to a dictionary.
264
+
265
+ Args:
266
+ issue: Dead code issue.
267
+
268
+ Returns:
269
+ Dictionary representation of the issue.
270
+ """
271
+ return {
272
+ "type": issue.type,
273
+ "file": issue.file,
274
+ "line": issue.line,
275
+ "name": issue.name,
276
+ "severity": issue.severity,
277
+ "fixed": issue.fixed,
278
+ }
279
+
280
+ def _suggestion_to_dict(self, suggestion: TestSuggestion) -> dict[str, Any]:
281
+ """Convert a TestSuggestion to a dictionary.
282
+
283
+ Args:
284
+ suggestion: Test suggestion.
285
+
286
+ Returns:
287
+ Dictionary representation of the suggestion.
288
+ """
289
+ return {
290
+ "function": suggestion.function,
291
+ "file": suggestion.file,
292
+ "line": suggestion.line,
293
+ "suggestion": suggestion.suggestion,
294
+ "suggestion_zh": suggestion.suggestion_zh,
295
+ }
File without changes
@@ -0,0 +1,215 @@
1
+ """
2
+ Git LFS Cleaner
3
+ Git LFS清理器
4
+
5
+ This module handles cleaning of Git LFS deployment artifacts.
6
+ 此模块处理Git LFS部署产物的清理。
7
+ """
8
+
9
+ import shutil
10
+ import subprocess
11
+ from pathlib import Path
12
+
13
+ from wl_cli.utils.logging import log_info_pair
14
+
15
+ from ...utils.root import RootKind
16
+ from ...utils.root_fallback import current_root
17
+
18
+
19
+ def clean_lfs_artifacts(dry_run: bool = False, config: str | None = None) -> None:
20
+ """
21
+ Clean Git LFS deployment without deleting actual files
22
+ 清理Git LFS部署,不删除实际文件
23
+ """
24
+ log_info_pair("Cleaning Git LFS deployment...", "正在清理Git LFS部署...")
25
+
26
+ # 检查并移除嵌套的.git目录
27
+ _handle_nested_git_dirs(dry_run=dry_run)
28
+
29
+ # 清理.gitattributes文件中的LFS跟踪规则
30
+ _clean_gitattributes(dry_run=dry_run)
31
+
32
+ # 清理LFS lock配置文件
33
+ _clean_lock_config(dry_run=dry_run)
34
+
35
+ # 卸载Git LFS钩子
36
+ _uninstall_git_lfs()
37
+
38
+ log_info_pair("Git LFS deployment cleaning completed.", "Git LFS部署清理完成。")
39
+
40
+
41
+ def _find_nested_git_dirs() -> list[Path]:
42
+ """查找嵌套的.git目录(不包括根目录的.git)"""
43
+ current_dir = Path.cwd()
44
+ nested_git_dirs = []
45
+
46
+ for git_dir in current_dir.glob("**/.git"):
47
+ # 跳过根目录的.git文件夹
48
+ if git_dir.parent == current_dir:
49
+ continue
50
+ # 检查是否是目录
51
+ if git_dir.is_dir():
52
+ nested_git_dirs.append(git_dir)
53
+
54
+ return nested_git_dirs
55
+
56
+
57
+ def _remove_nested_git_dirs(nested_git_dirs: list[Path], dry_run: bool = False) -> None:
58
+ """移除嵌套的.git目录"""
59
+ log_info_pair(
60
+ "\nAutomatically removing nested .git directories...",
61
+ "\n正在自动移除嵌套的.git目录...",
62
+ )
63
+
64
+ for git_dir in nested_git_dirs:
65
+ try:
66
+ if dry_run:
67
+ log_info_pair(f"✓ Would remove: {git_dir}", f"✓ 将移除: {git_dir}")
68
+ else:
69
+ shutil.rmtree(git_dir)
70
+ log_info_pair(
71
+ f"✓ Successfully removed: {git_dir}", f"✓ 成功移除: {git_dir}"
72
+ )
73
+ except Exception as e:
74
+ log_info_pair(
75
+ f"✗ Failed to remove {git_dir}: {e}", f"✗ 移除失败 {git_dir}: {e}"
76
+ )
77
+
78
+
79
+ def _handle_nested_git_dirs(dry_run: bool = False) -> None:
80
+ """处理嵌套的.git目录"""
81
+ log_info_pair(
82
+ "Checking for nested .git directories...", "正在检查嵌套的.git目录..."
83
+ )
84
+
85
+ nested_git_dirs = _find_nested_git_dirs()
86
+
87
+ if nested_git_dirs:
88
+ log_info_pair(
89
+ "Found nested .git directories, removing them to fix Git operations:",
90
+ "发现嵌套的.git目录,正在移除它们以修复Git操作:",
91
+ )
92
+
93
+ for git_dir in nested_git_dirs:
94
+ log_info_pair(f" - {git_dir}", f" - {git_dir}")
95
+
96
+ _remove_nested_git_dirs(nested_git_dirs, dry_run=dry_run)
97
+ else:
98
+ log_info_pair("✓ No nested .git directories found", "✓ 未发现嵌套的.git目录")
99
+
100
+
101
+ def _clean_gitattributes(dry_run: bool = False) -> None:
102
+ """清理.gitattributes文件中的LFS跟踪规则"""
103
+ gitattributes_path = Path(".gitattributes")
104
+ if gitattributes_path.exists():
105
+ log_info_pair(
106
+ "Removing Git LFS tracking rules from .gitattributes...",
107
+ "正在从.gitattributes中移除Git LFS跟踪规则...",
108
+ )
109
+
110
+ # Read the current content
111
+ with open(gitattributes_path) as f:
112
+ lines = f.readlines()
113
+
114
+ # Filter out Git LFS tracking rules
115
+ filtered_lines = []
116
+ for line in lines:
117
+ # Check if the line contains Git LFS tracking rules
118
+ if (
119
+ "filter=lfs" not in line
120
+ and "diff=lfs" not in line
121
+ and "merge=lfs" not in line
122
+ ):
123
+ filtered_lines.append(line)
124
+
125
+ # Write back the filtered content if not in dry run mode
126
+ if dry_run:
127
+ log_info_pair(
128
+ "✓ Would update .gitattributes to remove LFS tracking rules",
129
+ "✓ 将更新.gitattributes以移除LFS跟踪规则",
130
+ )
131
+ else:
132
+ with open(gitattributes_path, "w") as f:
133
+ f.writelines(filtered_lines)
134
+ log_info_pair(
135
+ "✓ Removed Git LFS tracking rules from .gitattributes",
136
+ "✓ 已从.gitattributes中移除Git LFS跟踪规则",
137
+ )
138
+
139
+
140
+ def _uninstall_git_lfs() -> None:
141
+ """卸载Git LFS钩子"""
142
+ log_info_pair("Removing Git LFS hooks...", "正在移除Git LFS钩子...")
143
+
144
+ try:
145
+ result = subprocess.run(
146
+ ["git", "lfs", "uninstall"], check=True, capture_output=True, text=True
147
+ )
148
+
149
+ # Filter out warning messages from both stdout and stderr
150
+ def filter_warnings(output):
151
+ if not output:
152
+ return ""
153
+ lines = output.strip().split("\n")
154
+ filtered_lines = [line for line in lines if not line.startswith("warning:")]
155
+ return "\n".join(filtered_lines).strip()
156
+
157
+ # Filter both stdout and stderr
158
+ filtered_stdout = filter_warnings(result.stdout)
159
+ filtered_stderr = filter_warnings(result.stderr)
160
+
161
+ # Combine filtered outputs if both exist
162
+ combined_output = ""
163
+ if filtered_stdout:
164
+ combined_output = filtered_stdout
165
+ if filtered_stderr:
166
+ if combined_output:
167
+ combined_output += f"\n{filtered_stderr}"
168
+ else:
169
+ combined_output = filtered_stderr
170
+
171
+ # Only log if there's meaningful output, otherwise just confirm success
172
+ if combined_output:
173
+ log_info_pair(
174
+ f"✓ Git LFS hooks removed: {combined_output}",
175
+ f"✓ Git LFS钩子已移除: {combined_output}",
176
+ )
177
+ else:
178
+ log_info_pair("✓ Git LFS hooks removed", "✓ Git LFS钩子已移除")
179
+ except subprocess.CalledProcessError as e:
180
+ # Filter warnings from stderr in case of error too
181
+ stderr_msg = e.stderr.strip() if e.stderr else "Unknown error"
182
+ filtered_stderr = ""
183
+ if stderr_msg:
184
+ lines = stderr_msg.split("\n")
185
+ filtered_lines = [line for line in lines if not line.startswith("warning:")]
186
+ filtered_stderr = "\n".join(filtered_lines).strip()
187
+ log_info_pair(
188
+ f"✗ Failed to remove Git LFS hooks: {filtered_stderr or stderr_msg}",
189
+ f"✗ 移除Git LFS钩子失败: {filtered_stderr or stderr_msg}",
190
+ )
191
+
192
+
193
+ def _clean_lock_config(dry_run: bool = False) -> None:
194
+ """Remove the project root's ``.wl/lfs-lock.yml`` if it exists.
195
+
196
+ Before #125 the ``.wl`` here was anchored to the **process working
197
+ directory** -- a bare relative ``Path(".wl")`` -- so ``wl clean`` from a
198
+ subdirectory looked for the *subdirectory's* lock config and left the root's
199
+ in place. #125 (the CWD-anchored -> root-anchored behaviour change, per the
200
+ #121 ruling) anchors it at the project root instead; see ADR 0006. The
201
+ ``PROJECT`` kind matches the LFS initializer that writes the file.
202
+ """
203
+ lock_config_path = current_root(RootKind.PROJECT).wl_dir() / "lfs-lock.yml"
204
+ if not lock_config_path.exists():
205
+ return
206
+
207
+ log_info_pair("Removing LFS lock configuration...", "正在移除LFS lock配置...")
208
+
209
+ if dry_run:
210
+ log_info_pair(
211
+ f"✓ Would remove: {lock_config_path}", f"✓ 将移除: {lock_config_path}"
212
+ )
213
+ else:
214
+ lock_config_path.unlink()
215
+ log_info_pair("✓ LFS lock configuration removed", "✓ LFS lock配置已移除")
@@ -0,0 +1,81 @@
1
+ """
2
+ Python-specific cleaning utilities for clean command.
3
+ """
4
+
5
+ import glob
6
+ import os
7
+ import shutil
8
+ from pathlib import Path
9
+
10
+ from wl_cli.utils.logging import log_info_pair
11
+
12
+
13
+ def remove_log_files(dry_run: bool = False) -> None:
14
+ """Remove log files."""
15
+ try:
16
+ for log_file in glob.glob("*.log"):
17
+ if dry_run:
18
+ log_info_pair(
19
+ f"Would remove log file: {log_file}", f"将删除日志文件: {log_file}"
20
+ )
21
+ else:
22
+ os.remove(log_file)
23
+ log_info_pair(
24
+ f"Removed log file: {log_file}", f"已删除日志文件: {log_file}"
25
+ )
26
+ except Exception as e:
27
+ log_info_pair(f"Failed to remove log files: {e}", f"删除日志文件失败: {e}")
28
+
29
+
30
+ def remove_pycache_dirs(dry_run: bool = False) -> None:
31
+ """Remove __pycache__ directories only in project directory (not in venv)."""
32
+ try:
33
+ project_root = Path(".").resolve()
34
+ for pycache_dir in project_root.rglob("__pycache__"):
35
+ # Skip pycache directories in virtual environments
36
+ if ".venv" in str(pycache_dir) or "venv" in str(pycache_dir):
37
+ continue
38
+
39
+ if pycache_dir.is_dir():
40
+ if dry_run:
41
+ log_info_pair(
42
+ f"Would remove pycache directory: {pycache_dir}",
43
+ f"将删除pycache目录: {pycache_dir}",
44
+ )
45
+ else:
46
+ shutil.rmtree(pycache_dir)
47
+ log_info_pair(
48
+ f"Removed pycache directory: {pycache_dir}",
49
+ f"已删除pycache目录: {pycache_dir}",
50
+ )
51
+ except Exception as e:
52
+ log_info_pair(
53
+ f"Failed to remove pycache directories: {e}", f"删除pycache目录失败: {e}"
54
+ )
55
+
56
+
57
+ def remove_egg_info_dirs(dry_run: bool = False) -> None:
58
+ """Remove egg-info directories only in project directory."""
59
+ try:
60
+ project_root = Path(".").resolve()
61
+ for egg_info_dir in project_root.rglob("*.egg-info"):
62
+ # Skip egg-info directories in virtual environments
63
+ if ".venv" in str(egg_info_dir) or "venv" in str(egg_info_dir):
64
+ continue
65
+
66
+ if egg_info_dir.is_dir():
67
+ if dry_run:
68
+ log_info_pair(
69
+ f"Would remove egg-info directory: {egg_info_dir}",
70
+ f"将删除egg-info目录: {egg_info_dir}",
71
+ )
72
+ else:
73
+ shutil.rmtree(egg_info_dir)
74
+ log_info_pair(
75
+ f"Removed egg-info directory: {egg_info_dir}",
76
+ f"已删除egg-info目录: {egg_info_dir}",
77
+ )
78
+ except Exception as e:
79
+ log_info_pair(
80
+ f"Failed to remove egg-info directories: {e}", f"删除egg-info目录失败: {e}"
81
+ )
@@ -0,0 +1,91 @@
1
+ """
2
+ Rust-specific cleaning utilities for clean command.
3
+ """
4
+
5
+ import os
6
+ import shutil
7
+
8
+ from wl_cli.utils.logging import log_info_pair
9
+
10
+
11
+ def remove_rust_analyzer_dirs(dry_run: bool = False) -> None:
12
+ """Remove rust-analyzer directories."""
13
+ rust_analyzer_dirs = ["target/rust-analyzer", "rust/target/rust-analyzer"]
14
+ for dir_path in rust_analyzer_dirs:
15
+ if os.path.exists(dir_path):
16
+ try:
17
+ if dry_run:
18
+ log_info_pair(
19
+ f"Would remove rust-analyzer directory: {dir_path}",
20
+ f"将删除rust-analyzer目录: {dir_path}",
21
+ )
22
+ else:
23
+ shutil.rmtree(dir_path)
24
+ log_info_pair(
25
+ f"Removed rust-analyzer directory: {dir_path}",
26
+ f"已删除rust-analyzer目录: {dir_path}",
27
+ )
28
+ except Exception as e:
29
+ log_info_pair(
30
+ f"Failed to remove rust-analyzer directory {dir_path}: {e}",
31
+ f"删除rust-analyzer目录 {dir_path} 失败: {e}",
32
+ )
33
+
34
+
35
+ def clean_rust_artifacts(dry_run: bool = False) -> None:
36
+ """
37
+ Clean Rust build artifacts
38
+ 清理Rust构建产物
39
+ """
40
+ # Check if Rust is enabled and directory exists
41
+ rust_dir = "rust"
42
+ if os.path.exists(rust_dir):
43
+ rust_target_dir = os.path.join(rust_dir, "target")
44
+ if os.path.exists(rust_target_dir):
45
+ try:
46
+ if dry_run:
47
+ log_info_pair(
48
+ f"Would remove Rust target directory: {rust_target_dir}",
49
+ f"将删除Rust target目录: {rust_target_dir}",
50
+ )
51
+ else:
52
+ shutil.rmtree(rust_target_dir)
53
+ log_info_pair(
54
+ f"Removed Rust target directory: {rust_target_dir}",
55
+ f"已删除Rust target目录: {rust_target_dir}",
56
+ )
57
+ except Exception as e:
58
+ log_info_pair(
59
+ f"Failed to remove Rust target directory {rust_target_dir}: {e}",
60
+ f"删除Rust target目录 {rust_target_dir} 失败: {e}",
61
+ )
62
+ else:
63
+ log_info_pair(
64
+ "Rust target directory does not exist, skipping...",
65
+ "Rust target目录不存在,跳过...",
66
+ )
67
+ else:
68
+ log_info_pair(
69
+ "Rust directory does not exist, skipping...", "Rust目录不存在,跳过..."
70
+ )
71
+
72
+ # Remove rust-analyzer directory in root target
73
+ root_rust_analyzer_dir = "target/rust-analyzer"
74
+ if os.path.exists(root_rust_analyzer_dir):
75
+ try:
76
+ if dry_run:
77
+ log_info_pair(
78
+ f"Would remove root rust-analyzer directory: {root_rust_analyzer_dir}",
79
+ f"将删除根目录rust-analyzer目录: {root_rust_analyzer_dir}",
80
+ )
81
+ else:
82
+ shutil.rmtree(root_rust_analyzer_dir)
83
+ log_info_pair(
84
+ f"Removed root rust-analyzer directory: {root_rust_analyzer_dir}",
85
+ f"已删除根目录rust-analyzer目录: {root_rust_analyzer_dir}",
86
+ )
87
+ except Exception as e:
88
+ log_info_pair(
89
+ f"Failed to remove root rust-analyzer directory {root_rust_analyzer_dir}: {e}",
90
+ f"删除根目录rust-analyzer目录 {root_rust_analyzer_dir} 失败: {e}",
91
+ )