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.
- py_wlcli-0.8.0.dist-info/METADATA +139 -0
- py_wlcli-0.8.0.dist-info/RECORD +320 -0
- py_wlcli-0.8.0.dist-info/WHEEL +4 -0
- py_wlcli-0.8.0.dist-info/entry_points.txt +2 -0
- py_wlcli-0.8.0.dist-info/licenses/LICENSE +21 -0
- wl_cli/__init__.py +27 -0
- wl_cli/__main__.py +130 -0
- wl_cli/capabilities.py +79 -0
- wl_cli/cc/__init__.py +5 -0
- wl_cli/cc/checks.py +1019 -0
- wl_cli/cc/cli.py +370 -0
- wl_cli/cc/mutate.py +1350 -0
- wl_cli/cli_click.py +156 -0
- wl_cli/commands/__init__.py +84 -0
- wl_cli/commands/base.py +80 -0
- wl_cli/commands/buildcommands/__init__.py +1 -0
- wl_cli/commands/buildcommands/build.py +71 -0
- wl_cli/commands/buildcommands/build_utils.py +47 -0
- wl_cli/commands/buildcommands/builders/__init__.py +18 -0
- wl_cli/commands/buildcommands/builders/cleanup.py +35 -0
- wl_cli/commands/buildcommands/builders/coordinator.py +122 -0
- wl_cli/commands/buildcommands/builders/environment.py +81 -0
- wl_cli/commands/buildcommands/builders/rust_builder.py +39 -0
- wl_cli/commands/buildcommands/builders/stubs.py +58 -0
- wl_cli/commands/buildcommands/dist.py +326 -0
- wl_cli/commands/buildcommands/test.py +89 -0
- wl_cli/commands/clean/__init__.py +1 -0
- wl_cli/commands/clean/build.py +151 -0
- wl_cli/commands/clean/clean.py +294 -0
- wl_cli/commands/clean/clean_utils.py +77 -0
- wl_cli/commands/clean/core.py +236 -0
- wl_cli/commands/clean/dead_code/__init__.py +0 -0
- wl_cli/commands/clean/dead_code/dead_code_command.py +70 -0
- wl_cli/commands/clean/dead_code/detector.py +161 -0
- wl_cli/commands/clean/dead_code/fixer.py +176 -0
- wl_cli/commands/clean/dead_code/reporter.py +295 -0
- wl_cli/commands/clean/dead_code/utils.py +0 -0
- wl_cli/commands/clean/lfs.py +215 -0
- wl_cli/commands/clean/python.py +81 -0
- wl_cli/commands/clean/rust.py +91 -0
- wl_cli/commands/clean/utils/__init__.py +34 -0
- wl_cli/commands/clean/venv.py +91 -0
- wl_cli/commands/cleanup.py +73 -0
- wl_cli/commands/click_base.py +79 -0
- wl_cli/commands/config/__init__.py +1 -0
- wl_cli/commands/config/config.py +291 -0
- wl_cli/commands/debug/__init__.py +6 -0
- wl_cli/commands/debug/debug.py +309 -0
- wl_cli/commands/debug/debug_cache.py +86 -0
- wl_cli/commands/debug/debug_subagent.py +156 -0
- wl_cli/commands/debug/output_formatter.py +165 -0
- wl_cli/commands/debug/source_hasher.py +92 -0
- wl_cli/commands/format/__init__.py +13 -0
- wl_cli/commands/format/format_cache_adapter.py +125 -0
- wl_cli/commands/format/format_command.py +197 -0
- wl_cli/commands/format/format_coordinator.py +191 -0
- wl_cli/commands/format/format_executor.py +470 -0
- wl_cli/commands/format/format_outcome.py +44 -0
- wl_cli/commands/format/format_path_handler.py +1016 -0
- wl_cli/commands/format/format_progress.py +62 -0
- wl_cli/commands/format/format_reporter.py +31 -0
- wl_cli/commands/format/python_formatter.py +506 -0
- wl_cli/commands/format/rust_formatter.py +106 -0
- wl_cli/commands/gate/__init__.py +1 -0
- wl_cli/commands/gate/gate_command.py +862 -0
- wl_cli/commands/gate/preflight.py +445 -0
- wl_cli/commands/git/__init__.py +1 -0
- wl_cli/commands/git/config.py +169 -0
- wl_cli/commands/git/git_facts.py +60 -0
- wl_cli/commands/git/git_flow.py +86 -0
- wl_cli/commands/git/hook.py +112 -0
- wl_cli/commands/git/policy.py +216 -0
- wl_cli/commands/git/push_update.py +82 -0
- wl_cli/commands/initenv/__init__.py +1 -0
- wl_cli/commands/initenv/common/__init__.py +23 -0
- wl_cli/commands/initenv/common/check_git_bash.py +78 -0
- wl_cli/commands/initenv/common/error_handler.py +68 -0
- wl_cli/commands/initenv/common/exceptions.py +52 -0
- wl_cli/commands/initenv/components/__init__.py +4 -0
- wl_cli/commands/initenv/components/cargo_sync.py +240 -0
- wl_cli/commands/initenv/components/config_manager.py +106 -0
- wl_cli/commands/initenv/components/directory_utils.py +71 -0
- wl_cli/commands/initenv/components/git/__init__.py +9 -0
- wl_cli/commands/initenv/components/git/branch_creator.py +143 -0
- wl_cli/commands/initenv/components/git/git_flow_interface.py +24 -0
- wl_cli/commands/initenv/components/git/git_flow_manager.py +159 -0
- wl_cli/commands/initenv/components/git/git_lfs_manager.py +272 -0
- wl_cli/commands/initenv/components/git/git_utility.py +254 -0
- wl_cli/commands/initenv/components/git/gitignore_manager.py +53 -0
- wl_cli/commands/initenv/components/git_initializer.py +212 -0
- wl_cli/commands/initenv/components/i18n_manager.py +104 -0
- wl_cli/commands/initenv/components/log_manager.py +241 -0
- wl_cli/commands/initenv/components/project_structure/__init__.py +23 -0
- wl_cli/commands/initenv/components/project_structure/config_generator.py +360 -0
- wl_cli/commands/initenv/components/project_structure/directory_creator.py +184 -0
- wl_cli/commands/initenv/components/project_structure/hook_source.py +110 -0
- wl_cli/commands/initenv/components/project_structure/hooks_manager.py +995 -0
- wl_cli/commands/initenv/components/project_structure/readme_handler.py +56 -0
- wl_cli/commands/initenv/components/project_structure/rust_files_handler.py +30 -0
- wl_cli/commands/initenv/components/project_structure/setup_handler.py +103 -0
- wl_cli/commands/initenv/components/project_structure/sop_generator.py +73 -0
- wl_cli/commands/initenv/components/project_structure/template_copier.py +104 -0
- wl_cli/commands/initenv/components/project_structure/template_manager.py +195 -0
- wl_cli/commands/initenv/components/pyproject_generator.py +194 -0
- wl_cli/commands/initenv/components/rust_initializer.py +145 -0
- wl_cli/commands/initenv/components/symlink_cleaner.py +99 -0
- wl_cli/commands/initenv/components/symlink_manager.py +52 -0
- wl_cli/commands/initenv/components/venv_manager.py +118 -0
- wl_cli/commands/initenv/initenv.py +169 -0
- wl_cli/commands/initenv/services/__init__.py +11 -0
- wl_cli/commands/initenv/services/environment_initializer.py +288 -0
- wl_cli/commands/initenv/services/git_lfs_initializer.py +26 -0
- wl_cli/commands/initenv/services/project_structure_initializer.py +52 -0
- wl_cli/commands/lfs/README.md +221 -0
- wl_cli/commands/lfs/__init__.py +5 -0
- wl_cli/commands/lfs/cli.py +106 -0
- wl_cli/commands/lfs/config.py +164 -0
- wl_cli/commands/lfs/hook.py +61 -0
- wl_cli/commands/lfs/initializer.py +531 -0
- wl_cli/commands/lfs/manager.py +95 -0
- wl_cli/commands/lfs/policy.py +165 -0
- wl_cli/commands/lfs/watcher.py +294 -0
- wl_cli/commands/lint/__init__.py +1 -0
- wl_cli/commands/lint/lint_command.py +297 -0
- wl_cli/commands/lint/lint_executor.py +409 -0
- wl_cli/commands/lint/lint_reporter.py +579 -0
- wl_cli/commands/lint/lint_run.py +258 -0
- wl_cli/commands/lint/mypy_config.py +90 -0
- wl_cli/commands/registry.py +103 -0
- wl_cli/commands/self/__init__.py +7 -0
- wl_cli/commands/self/common/__init__.py +8 -0
- wl_cli/commands/self/common/capabilities.py +30 -0
- wl_cli/commands/self/common/config.py +27 -0
- wl_cli/commands/self/common/version_cache.py +85 -0
- wl_cli/commands/self/handlers/__init__.py +19 -0
- wl_cli/commands/self/handlers/base_handler.py +28 -0
- wl_cli/commands/self/handlers/install_handler.py +85 -0
- wl_cli/commands/self/handlers/status_handler.py +66 -0
- wl_cli/commands/self/handlers/uninstall_handler.py +58 -0
- wl_cli/commands/self/handlers/update_handler.py +102 -0
- wl_cli/commands/self/handlers/version_handler.py +16 -0
- wl_cli/commands/self/self_command.py +237 -0
- wl_cli/commands/self/services/__init__.py +15 -0
- wl_cli/commands/self/services/installation_manager.py +187 -0
- wl_cli/commands/self/services/local_installer.py +165 -0
- wl_cli/commands/self/services/pypi_updater.py +278 -0
- wl_cli/commands/self/services/windows_update.py +174 -0
- wl_cli/commands/testcommands/__init__.py +8 -0
- wl_cli/commands/testcommands/base_dependency_analyzer.py +198 -0
- wl_cli/commands/testcommands/batch_processor.py +278 -0
- wl_cli/commands/testcommands/cache_manager.py +1156 -0
- wl_cli/commands/testcommands/coverage_analyzer.py +955 -0
- wl_cli/commands/testcommands/coverage_cache_manager.py +183 -0
- wl_cli/commands/testcommands/progress_manager.py +79 -0
- wl_cli/commands/testcommands/python_env_detector.py +207 -0
- wl_cli/commands/testcommands/report_generator.py +101 -0
- wl_cli/commands/testcommands/single_test_executor.py +250 -0
- wl_cli/commands/testcommands/speed_checker.py +118 -0
- wl_cli/commands/testcommands/test_command_builder.py +165 -0
- wl_cli/commands/testcommands/test_commands.py +786 -0
- wl_cli/commands/testcommands/test_reporter.py +181 -0
- wl_cli/commands/testcommands/test_result_handler.py +96 -0
- wl_cli/commands/testcommands/test_runner.py +1343 -0
- wl_cli/commands/testcommands/test_utils.py +197 -0
- wl_cli/commands/testcommands/utils/__init__.py +17 -0
- wl_cli/commands/testcommands/utils/coverage_parser.py +98 -0
- wl_cli/commands/testcommands/utils/env_info.py +36 -0
- wl_cli/commands/testcommands/utils/junit_parser.py +75 -0
- wl_cli/commands/testcommands/utils/markdown_utils.py +20 -0
- wl_cli/commands/testcommands/utils/project_root.py +61 -0
- wl_cli/commands/update/__init__.py +9 -0
- wl_cli/commands/update/services/__init__.py +21 -0
- wl_cli/commands/update/services/backup_manager.py +207 -0
- wl_cli/commands/update/services/component_manager.py +83 -0
- wl_cli/commands/update/services/conflict_resolver.py +189 -0
- wl_cli/commands/update/services/vendors_updater.py +85 -0
- wl_cli/commands/update/services/version_tracker.py +127 -0
- wl_cli/commands/update/update_command.py +270 -0
- wl_cli/commands/update/utils/__init__.py +3 -0
- wl_cli/commands/version/__init__.py +9 -0
- wl_cli/commands/version/version_command.py +155 -0
- wl_cli/commands/workflow.py +477 -0
- wl_cli/env/__init__.py +5 -0
- wl_cli/env/cli.py +167 -0
- wl_cli/env_status.py +157 -0
- wl_cli/envelope.py +91 -0
- wl_cli/exceptions.py +85 -0
- wl_cli/lib/__init__.py +3 -0
- wl_cli/lib/rust_utils.py +35 -0
- wl_cli/mcp/__init__.py +6 -0
- wl_cli/mcp/capabilities.py +19 -0
- wl_cli/mcp/cli.py +22 -0
- wl_cli/mcp/gitlab.py +553 -0
- wl_cli/mcp/k8s.py +344 -0
- wl_cli/mcp/protocol.py +159 -0
- wl_cli/platform.py +139 -0
- wl_cli/quality/__init__.py +18 -0
- wl_cli/utils/__init__.py +7 -0
- wl_cli/utils/cache.py +491 -0
- wl_cli/utils/cache_ledger.py +355 -0
- wl_cli/utils/cache_store.py +546 -0
- wl_cli/utils/config/__init__.py +27 -0
- wl_cli/utils/config/config_loader.py +163 -0
- wl_cli/utils/config/core.py +862 -0
- wl_cli/utils/config/defaults.py +142 -0
- wl_cli/utils/config/env_exporter.py +86 -0
- wl_cli/utils/config/env_parser.py +108 -0
- wl_cli/utils/config/validation.py +280 -0
- wl_cli/utils/error_handler.py +165 -0
- wl_cli/utils/file_copier.py +56 -0
- wl_cli/utils/file_operations.py +506 -0
- wl_cli/utils/language_utils.py +122 -0
- wl_cli/utils/lazy_import.py +240 -0
- wl_cli/utils/log_levels.py +35 -0
- wl_cli/utils/log_rotators.py +74 -0
- wl_cli/utils/logging.py +458 -0
- wl_cli/utils/output_context.py +142 -0
- wl_cli/utils/performance/__init__.py +1 -0
- wl_cli/utils/performance/decorators.py +111 -0
- wl_cli/utils/performance/exporter.py +38 -0
- wl_cli/utils/performance/models.py +16 -0
- wl_cli/utils/performance/monitor.py +161 -0
- wl_cli/utils/performance_monitor.py +32 -0
- wl_cli/utils/platform_adapter.py +131 -0
- wl_cli/utils/pre_commit.py +266 -0
- wl_cli/utils/pre_commit_config.py +72 -0
- wl_cli/utils/root.py +521 -0
- wl_cli/utils/root_fallback.py +256 -0
- wl_cli/utils/run_evidence.py +236 -0
- wl_cli/utils/structured_logger.py +18 -0
- wl_cli/utils/structured_logging/__init__.py +19 -0
- wl_cli/utils/structured_logging/config.py +337 -0
- wl_cli/utils/structured_logging/core.py +177 -0
- wl_cli/utils/structured_logging/env.py +56 -0
- wl_cli/utils/structured_logging/filtering.py +75 -0
- wl_cli/utils/structured_logging/filters.py +55 -0
- wl_cli/utils/structured_logging/formatters.py +251 -0
- wl_cli/utils/structured_logging/handlers.py +308 -0
- wl_cli/utils/structured_logging/logger.py +354 -0
- wl_cli/utils/subprocess.py +71 -0
- wl_cli/utils/subprocess_utils.py +260 -0
- wl_cli/utils/test_utils/__init__.py +5 -0
- wl_cli/utils/test_utils/test_filter.py +36 -0
- wl_cli/utils/uv_run.py +79 -0
- wl_cli/utils/uv_tool.py +117 -0
- wl_cli/utils/uv_tree.py +71 -0
- wl_cli/utils/uv_workspace.py +174 -0
- wl_cli/utils/version/__init__.py +15 -0
- wl_cli/utils/version/pypi_version_checker.py +95 -0
- wl_cli/utils/version/version_comparator.py +133 -0
- wl_cli/utils/version/version_detectors.py +312 -0
- wl_cli/utils/version/version_service.py +131 -0
- wl_cli/utils/version/version_updater.py +198 -0
- wl_cli/utils/wl_dir_updater.py +1440 -0
- wl_cli/utils/workspace_detector/__init__.py +28 -0
- wl_cli/utils/workspace_detector/core.py +161 -0
- wl_cli/utils/workspace_detector/detection_strategies.py +107 -0
- wl_cli/utils/workspace_detector/exceptions.py +5 -0
- wl_cli/utils/workspace_detector/types.py +17 -0
- wl_cli/utils/workspace_detector/venv_resolver.py +84 -0
- wl_cli/vendors/config/.codespellrc +2 -0
- wl_cli/vendors/config/dependencies.toml +10 -0
- wl_cli/vendors/config/mypy.ini +62 -0
- wl_cli/vendors/config/pyproject_defaults.toml +28 -0
- wl_cli/vendors/config/template_config.toml +24 -0
- wl_cli/vendors/config/tool_configs.toml +76 -0
- wl_cli/vendors/git/.gitignore +122 -0
- wl_cli/vendors/git/.pre-commit-config.yaml +37 -0
- wl_cli/vendors/hooks/applypatch-msg.sample +15 -0
- wl_cli/vendors/hooks/commit-msg.sample +24 -0
- wl_cli/vendors/hooks/fsmonitor-watchman.sample +174 -0
- wl_cli/vendors/hooks/post-update.sample +8 -0
- wl_cli/vendors/hooks/pre-applypatch.sample +14 -0
- wl_cli/vendors/hooks/pre-commit +45 -0
- wl_cli/vendors/hooks/pre-commit.legacy +31 -0
- wl_cli/vendors/hooks/pre-commit.sample +49 -0
- wl_cli/vendors/hooks/pre-merge-commit.sample +13 -0
- wl_cli/vendors/hooks/pre-push.sample +60 -0
- wl_cli/vendors/hooks/pre-rebase.sample +169 -0
- wl_cli/vendors/hooks/pre-receive.sample +24 -0
- wl_cli/vendors/hooks/prepare-commit-msg.sample +42 -0
- wl_cli/vendors/hooks/push-to-checkout.sample +78 -0
- wl_cli/vendors/hooks/sendemail-validate.sample +77 -0
- wl_cli/vendors/hooks/unix/applypatch-msg.sample +15 -0
- wl_cli/vendors/hooks/unix/check_lockfile.py +58 -0
- wl_cli/vendors/hooks/unix/check_lockfile.sh +36 -0
- wl_cli/vendors/hooks/unix/commit-msg.sample +24 -0
- wl_cli/vendors/hooks/unix/fsmonitor-watchman.sample +174 -0
- wl_cli/vendors/hooks/unix/post-update.sample +8 -0
- wl_cli/vendors/hooks/unix/pre-applypatch.sample +14 -0
- wl_cli/vendors/hooks/unix/pre-commit +190 -0
- wl_cli/vendors/hooks/unix/pre-commit.legacy +31 -0
- wl_cli/vendors/hooks/unix/pre-commit.py +81 -0
- wl_cli/vendors/hooks/unix/pre-commit.sample +49 -0
- wl_cli/vendors/hooks/unix/pre-merge-commit.sample +13 -0
- wl_cli/vendors/hooks/unix/pre-push +417 -0
- wl_cli/vendors/hooks/unix/pre-push.sample +53 -0
- wl_cli/vendors/hooks/unix/pre-rebase.sample +169 -0
- wl_cli/vendors/hooks/unix/pre-receive.sample +24 -0
- wl_cli/vendors/hooks/unix/prepare-commit-msg.sample +42 -0
- wl_cli/vendors/hooks/update.sample +128 -0
- wl_cli/vendors/hooks/win/work.bat +63 -0
- wl_cli/vendors/project/.gitignore +90 -0
- wl_cli/vendors/project/.python-version +1 -0
- wl_cli/vendors/project/LICENSE +21 -0
- wl_cli/vendors/project/README.md +0 -0
- wl_cli/vendors/project/pyproject.toml +27 -0
- wl_cli/vendors/project/pyproject_no_workspace.toml +16 -0
- wl_cli/vendors/project/pyproject_workspace.toml +14 -0
- wl_cli/vendors/readme/README.md +120 -0
- wl_cli/vendors/rust/Cargo.toml +14 -0
- wl_cli/vendors/rust/lib/__init__.py +3 -0
- wl_cli/vendors/rust/lib/rust_utils.py +31 -0
- wl_cli/vendors/rust/src/lib.rs +13 -0
- wl_cli/vendors/rust/src/main.rs +3 -0
- wl_cli/vendors/rust/tests/test_rust_fallback.py +40 -0
- wl_cli/vendors/sop_doc/git-dev-sop.md +220 -0
- wl_cli/vendors/sop_doc/industrial-standard-sop.md +145 -0
- wl_cli/vendors/sop_doc/python-sop.md +342 -0
- wl_cli/vendors/sop_doc/rust-readme.md +219 -0
wl_cli/cc/cli.py
ADDED
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
"""Click namespace for the ``cc`` domain (issues #220, #222, #223).
|
|
2
|
+
|
|
3
|
+
``wl cc check`` reports the Claude Code host state as the shared five-state
|
|
4
|
+
envelope :mod:`wl_cli.envelope`. The six host sections are read-only (see
|
|
5
|
+
:mod:`wl_cli.cc.checks`); the nested ``env`` field reuses
|
|
6
|
+
:func:`wl_cli.env_status.build_payload` in this process, so ``wl cc check``
|
|
7
|
+
and ``wl env check`` cannot answer the environment question differently.
|
|
8
|
+
|
|
9
|
+
``wl cc init`` / ``wl cc repair`` / ``wl cc upgrade`` and the granular
|
|
10
|
+
``plugins|mcp|settings|statusline|hooks|stamps`` commands are the write side
|
|
11
|
+
(:mod:`wl_cli.cc.mutate`). They reuse the very same probes: probe first, then
|
|
12
|
+
act only on the difference -- so a second ``init`` is a no-op and ``check``
|
|
13
|
+
can never disagree with ``repair``.
|
|
14
|
+
|
|
15
|
+
``cc`` is allowed to import ``wl_cli.env`` (one-way), but ``env``/``mcp``/
|
|
16
|
+
``quality`` may not import ``wl_cli.cc`` -- enforced by
|
|
17
|
+
``tests/test_domain_import_direction.py``.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
from typing import TYPE_CHECKING, Any
|
|
23
|
+
|
|
24
|
+
import click
|
|
25
|
+
|
|
26
|
+
from ..envelope import OK_STATUSES, dump, exit_code
|
|
27
|
+
from . import mutate
|
|
28
|
+
from .checks import TOOL, build_report
|
|
29
|
+
|
|
30
|
+
if TYPE_CHECKING:
|
|
31
|
+
from collections.abc import Callable
|
|
32
|
+
|
|
33
|
+
#: Host sections rendered by the human-readable summary, in payload order.
|
|
34
|
+
_HOST_SECTIONS = ("plugins", "mcp", "settings", "stamps", "hooks", "statusline")
|
|
35
|
+
|
|
36
|
+
_JSON_HELP = "Emit one five-state JSON envelope on stdout."
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _section_line(name: str, section: dict[str, Any]) -> str:
|
|
40
|
+
"""Render one ``name: status -- reason`` line."""
|
|
41
|
+
reason = section.get("reason")
|
|
42
|
+
suffix = f" -- {reason}" if reason else ""
|
|
43
|
+
return f"{name}: {section.get('status')}{suffix}"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _summary_lines(
|
|
47
|
+
status: str, reason: str | None, sections: dict[str, Any]
|
|
48
|
+
) -> list[str]:
|
|
49
|
+
"""Render the human-readable ``check`` summary (no ``--json``)."""
|
|
50
|
+
lines = [f"wl cc check: {status}"]
|
|
51
|
+
env = sections.get("env", {})
|
|
52
|
+
if env.get("status") == "ok":
|
|
53
|
+
lines.append(f"env: ok (wl {env.get('version')}, Python {env.get('python')})")
|
|
54
|
+
else:
|
|
55
|
+
lines.append(_section_line("env", env))
|
|
56
|
+
lines.extend(_section_line(name, sections.get(name, {})) for name in _HOST_SECTIONS)
|
|
57
|
+
if reason:
|
|
58
|
+
lines.append(f"reason: {reason}")
|
|
59
|
+
return lines
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _split(result: dict[str, Any]) -> tuple[str, str | None, dict[str, Any]]:
|
|
63
|
+
"""Split a mutation result into ``(status, reason, payload)``."""
|
|
64
|
+
reason = result.get("reason")
|
|
65
|
+
fields = {
|
|
66
|
+
key: value for key, value in result.items() if key not in ("status", "reason")
|
|
67
|
+
}
|
|
68
|
+
return str(result["status"]), reason if isinstance(reason, str) else None, fields
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _changed_lines(changed: Any) -> list[str]:
|
|
72
|
+
"""Render the ``changed`` action list; an empty list says so explicitly."""
|
|
73
|
+
items = [str(item) for item in changed] if isinstance(changed, list) else []
|
|
74
|
+
if not items:
|
|
75
|
+
return ["changed: none"]
|
|
76
|
+
return ["changed:", *(f" - {item}" for item in items)]
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _summary(
|
|
80
|
+
tool: str, status: str, reason: str | None, fields: dict[str, Any]
|
|
81
|
+
) -> list[str]:
|
|
82
|
+
"""Render one section command's human-readable summary."""
|
|
83
|
+
lines = [f"{tool}: {status}" + (f" -- {reason}" if reason else "")]
|
|
84
|
+
lines.extend(_changed_lines(fields.get("changed")))
|
|
85
|
+
return lines
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _report_summary(
|
|
89
|
+
tool: str, status: str, reason: str | None, fields: dict[str, Any]
|
|
90
|
+
) -> list[str]:
|
|
91
|
+
"""Render ``init`` / ``repair``: the aggregate plus every section."""
|
|
92
|
+
lines = [f"{tool}: {status}" + (f" -- {reason}" if reason else "")]
|
|
93
|
+
if fields.get("dry_run"):
|
|
94
|
+
lines.append("dry run: nothing was written")
|
|
95
|
+
for name in mutate.SECTION_ORDER:
|
|
96
|
+
section = fields.get(name)
|
|
97
|
+
if isinstance(section, dict):
|
|
98
|
+
lines.append(_section_line(name, section))
|
|
99
|
+
lines.extend(_changed_lines(fields.get("changed")))
|
|
100
|
+
return lines
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _emit(
|
|
104
|
+
tool: str,
|
|
105
|
+
status: str,
|
|
106
|
+
reason: str | None,
|
|
107
|
+
fields: dict[str, Any],
|
|
108
|
+
*,
|
|
109
|
+
as_json: bool,
|
|
110
|
+
summary: list[str],
|
|
111
|
+
) -> None:
|
|
112
|
+
"""Write the envelope or the human summary, then exit with the status code."""
|
|
113
|
+
if as_json:
|
|
114
|
+
dump(status, status in OK_STATUSES, tool=tool, reason=reason, **fields)
|
|
115
|
+
else:
|
|
116
|
+
for line in summary:
|
|
117
|
+
click.echo(line)
|
|
118
|
+
raise SystemExit(exit_code(status))
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _run_single(tool: str, action: Callable[[], dict[str, Any]], as_json: bool) -> None:
|
|
122
|
+
"""Run one section mutation and report it."""
|
|
123
|
+
status, reason, fields = _split(action())
|
|
124
|
+
_emit(
|
|
125
|
+
tool,
|
|
126
|
+
status,
|
|
127
|
+
reason,
|
|
128
|
+
fields,
|
|
129
|
+
as_json=as_json,
|
|
130
|
+
summary=_summary(tool, status, reason, fields),
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
@click.group(
|
|
135
|
+
name="cc",
|
|
136
|
+
help="Claude Code integration helpers (machine-readable five-state JSON).",
|
|
137
|
+
)
|
|
138
|
+
def cc_group() -> None:
|
|
139
|
+
"""Claude Code domain entry point (``check`` plus the write commands)."""
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
@cc_group.command(name="check")
|
|
143
|
+
@click.option(
|
|
144
|
+
"--json",
|
|
145
|
+
"as_json",
|
|
146
|
+
is_flag=True,
|
|
147
|
+
help=_JSON_HELP,
|
|
148
|
+
)
|
|
149
|
+
def check(as_json: bool) -> None:
|
|
150
|
+
"""Report the Claude Code host state without writing anything.
|
|
151
|
+
|
|
152
|
+
The report nests the same-process ``wl env check`` payload under ``env``
|
|
153
|
+
and adds the read-only host sections ``plugins`` / ``mcp`` / ``settings``
|
|
154
|
+
/ ``stamps`` / ``hooks`` / ``statusline``. Every section carries its own
|
|
155
|
+
five-state ``status`` and a readable ``reason``; one failing section never
|
|
156
|
+
hides the others. In ``--json`` mode stdout is exactly one envelope line
|
|
157
|
+
and diagnostics go to stderr; the probe is read-only, and the dispatcher
|
|
158
|
+
skips the startup ``.wl`` self-sync (#220).
|
|
159
|
+
"""
|
|
160
|
+
status, reason, sections = build_report()
|
|
161
|
+
if as_json:
|
|
162
|
+
dump(status, status in OK_STATUSES, tool=TOOL, reason=reason, **sections)
|
|
163
|
+
raise SystemExit(exit_code(status))
|
|
164
|
+
|
|
165
|
+
for line in _summary_lines(status, reason, sections):
|
|
166
|
+
click.echo(line)
|
|
167
|
+
raise SystemExit(exit_code(status))
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
@cc_group.command(name="init")
|
|
171
|
+
@click.option("--json", "as_json", is_flag=True, help=_JSON_HELP)
|
|
172
|
+
@click.option(
|
|
173
|
+
"--dry-run",
|
|
174
|
+
is_flag=True,
|
|
175
|
+
help="Run every probe and report the plan without writing anything.",
|
|
176
|
+
)
|
|
177
|
+
def init_command(as_json: bool, dry_run: bool) -> None:
|
|
178
|
+
"""Provision the host and write the ``init-done`` stamp (idempotent).
|
|
179
|
+
|
|
180
|
+
Every section is probed first and only its difference is acted on, so a
|
|
181
|
+
second run is a no-op. ``env`` is reported but never mutated: installing
|
|
182
|
+
``wl`` itself is ``wl env repair``'s single literal. The ``init-done``
|
|
183
|
+
stamp is skipped when a section is ``error`` or ``unverifiable``.
|
|
184
|
+
"""
|
|
185
|
+
status, reason, fields = mutate.run_init(dry_run=dry_run)
|
|
186
|
+
_emit(
|
|
187
|
+
"cc init",
|
|
188
|
+
status,
|
|
189
|
+
reason,
|
|
190
|
+
fields,
|
|
191
|
+
as_json=as_json,
|
|
192
|
+
summary=_report_summary("cc init", status, reason, fields),
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
@cc_group.command(name="repair")
|
|
197
|
+
@click.option("--json", "as_json", is_flag=True, help=_JSON_HELP)
|
|
198
|
+
@click.option(
|
|
199
|
+
"--dry-run",
|
|
200
|
+
is_flag=True,
|
|
201
|
+
help="Run every probe and report the plan without writing anything.",
|
|
202
|
+
)
|
|
203
|
+
@click.option(
|
|
204
|
+
"--section",
|
|
205
|
+
"sections",
|
|
206
|
+
multiple=True,
|
|
207
|
+
type=click.Choice(mutate.MUTABLE_SECTIONS),
|
|
208
|
+
help="Repair only this section (repeatable; default: all of them).",
|
|
209
|
+
)
|
|
210
|
+
def repair_command(as_json: bool, dry_run: bool, sections: tuple[str, ...]) -> None:
|
|
211
|
+
"""Repair the selected sections; only the difference is written.
|
|
212
|
+
|
|
213
|
+
``disabled`` plugins are never re-enabled, an existing claude-mem
|
|
214
|
+
``settings.json`` and a conflicting statusline are never overwritten, and
|
|
215
|
+
the MCP stamp is written only when both servers registered. ``repaired``
|
|
216
|
+
is reported only for a section that actually wrote.
|
|
217
|
+
"""
|
|
218
|
+
status, reason, fields = mutate.run_repair(sections or None, dry_run=dry_run)
|
|
219
|
+
_emit(
|
|
220
|
+
"cc repair",
|
|
221
|
+
status,
|
|
222
|
+
reason,
|
|
223
|
+
fields,
|
|
224
|
+
as_json=as_json,
|
|
225
|
+
summary=_report_summary("cc repair", status, reason, fields),
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
@cc_group.command(name="upgrade")
|
|
230
|
+
@click.option("--json", "as_json", is_flag=True, help=_JSON_HELP)
|
|
231
|
+
def upgrade_command(as_json: bool) -> None:
|
|
232
|
+
"""Upgrade the wlcc Claude Code plugin (not the ``wl`` binary).
|
|
233
|
+
|
|
234
|
+
Refreshes the ``wlcc`` marketplace (adding it when unregistered) and runs
|
|
235
|
+
``claude plugin update wlcc@wlcc -s user``. The installed version comes
|
|
236
|
+
from ``installed_plugins.json``'s ``scope: user`` entry; a changed version
|
|
237
|
+
sets ``requires_restart``. ``wl self update`` / ``wl env repair`` own the
|
|
238
|
+
CLI itself and are deliberately a different command.
|
|
239
|
+
"""
|
|
240
|
+
_run_single("cc upgrade", mutate.upgrade_wlcc, as_json)
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
@cc_group.group(name="plugins")
|
|
244
|
+
def plugins_group() -> None:
|
|
245
|
+
"""Required-plugin provisioning (never re-enables a disabled plugin)."""
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
@plugins_group.command(name="ensure")
|
|
249
|
+
@click.option("--json", "as_json", is_flag=True, help=_JSON_HELP)
|
|
250
|
+
def plugins_ensure(as_json: bool) -> None:
|
|
251
|
+
"""Install only the missing required plugins.
|
|
252
|
+
|
|
253
|
+
Disabled plugins are already installed and are never re-enabled; only an
|
|
254
|
+
entry absent from the registry is installed.
|
|
255
|
+
"""
|
|
256
|
+
_run_single("cc plugins ensure", mutate.ensure_plugins, as_json)
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
@plugins_group.command(name="list")
|
|
260
|
+
@click.option("--json", "as_json", is_flag=True, help=_JSON_HELP)
|
|
261
|
+
def plugins_list(as_json: bool) -> None:
|
|
262
|
+
"""Report plugin presence and enable state without writing."""
|
|
263
|
+
_run_single("cc plugins list", lambda: mutate.probe_section("plugins"), as_json)
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
@cc_group.group(name="mcp")
|
|
267
|
+
def mcp_group() -> None:
|
|
268
|
+
"""MCP server registration and the version stamp."""
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
@mcp_group.command(name="ensure")
|
|
272
|
+
@click.option("--json", "as_json", is_flag=True, help=_JSON_HELP)
|
|
273
|
+
def mcp_ensure(as_json: bool) -> None:
|
|
274
|
+
"""Re-register both MCP servers; the stamp is written only on full success."""
|
|
275
|
+
_run_single("cc mcp ensure", mutate.ensure_mcp, as_json)
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
@mcp_group.command(name="list")
|
|
279
|
+
@click.option("--json", "as_json", is_flag=True, help=_JSON_HELP)
|
|
280
|
+
def mcp_list(as_json: bool) -> None:
|
|
281
|
+
"""Report the plugin root, interpreter, stamp and servers without writing."""
|
|
282
|
+
_run_single("cc mcp list", lambda: mutate.probe_section("mcp"), as_json)
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
@cc_group.group(name="settings")
|
|
286
|
+
def settings_group() -> None:
|
|
287
|
+
"""Host-side settings: gstack skill and claude-mem configuration."""
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
@settings_group.command(name="apply")
|
|
291
|
+
@click.option("--json", "as_json", is_flag=True, help=_JSON_HELP)
|
|
292
|
+
def settings_apply(as_json: bool) -> None:
|
|
293
|
+
"""Provision gstack and write claude-mem's settings only when they are absent.
|
|
294
|
+
|
|
295
|
+
An existing claude-mem ``settings.json`` is never overwritten; a differing
|
|
296
|
+
model stays a merge-it-yourself state.
|
|
297
|
+
"""
|
|
298
|
+
_run_single("cc settings apply", mutate.ensure_settings, as_json)
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
@settings_group.command(name="check")
|
|
302
|
+
@click.option("--json", "as_json", is_flag=True, help=_JSON_HELP)
|
|
303
|
+
def settings_check(as_json: bool) -> None:
|
|
304
|
+
"""Report gstack and claude-mem without writing."""
|
|
305
|
+
_run_single("cc settings check", lambda: mutate.probe_section("settings"), as_json)
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
@cc_group.group(name="statusline")
|
|
309
|
+
def statusline_group() -> None:
|
|
310
|
+
"""The claude-hud statusLine (merge-write, never overwrite a conflict)."""
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
@statusline_group.command(name="write")
|
|
314
|
+
@click.option("--json", "as_json", is_flag=True, help=_JSON_HELP)
|
|
315
|
+
def statusline_write(as_json: bool) -> None:
|
|
316
|
+
"""Write the claude-hud command; a conflicting statusline is never overwritten.
|
|
317
|
+
|
|
318
|
+
The write is backed up, atomically replaced and self-checked: every key
|
|
319
|
+
other than ``statusLine`` must stay byte-identical.
|
|
320
|
+
"""
|
|
321
|
+
_run_single("cc statusline write", mutate.write_statusline, as_json)
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
@statusline_group.command(name="check")
|
|
325
|
+
@click.option("--json", "as_json", is_flag=True, help=_JSON_HELP)
|
|
326
|
+
def statusline_check(as_json: bool) -> None:
|
|
327
|
+
"""Report the configured statusline without writing."""
|
|
328
|
+
_run_single(
|
|
329
|
+
"cc statusline check", lambda: mutate.probe_section("statusline"), as_json
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
@cc_group.group(name="hooks")
|
|
334
|
+
def hooks_group() -> None:
|
|
335
|
+
"""Dangling hook commands (rewrite preferred over prune)."""
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
@hooks_group.command(name="check")
|
|
339
|
+
@click.option("--json", "as_json", is_flag=True, help=_JSON_HELP)
|
|
340
|
+
def hooks_check(as_json: bool) -> None:
|
|
341
|
+
"""Report dangling hook commands without writing."""
|
|
342
|
+
_run_single("cc hooks check", lambda: mutate.probe_section("hooks"), as_json)
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
@hooks_group.command(name="repair")
|
|
346
|
+
@click.option("--json", "as_json", is_flag=True, help=_JSON_HELP)
|
|
347
|
+
def hooks_repair(as_json: bool) -> None:
|
|
348
|
+
"""Rewrite or prune dangling hook commands with a backup and self-check.
|
|
349
|
+
|
|
350
|
+
Rewrite wins over prune; only a command whose every dangling path has no
|
|
351
|
+
local counterpart is pruned. Nothing is written when the plan is identical
|
|
352
|
+
to the file.
|
|
353
|
+
"""
|
|
354
|
+
_run_single("cc hooks repair", mutate.repair_hooks, as_json)
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
@cc_group.group(name="stamps")
|
|
358
|
+
def stamps_group() -> None:
|
|
359
|
+
"""The wlcc stamp registry under ``~/.config/wlcc``."""
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
@stamps_group.command(name="check")
|
|
363
|
+
@click.option("--json", "as_json", is_flag=True, help=_JSON_HELP)
|
|
364
|
+
def stamps_check(as_json: bool) -> None:
|
|
365
|
+
"""Report retired and unregistered stamps without deleting anything.
|
|
366
|
+
|
|
367
|
+
Deleting the retired list is part of ``wl cc repair``; unregistered entries
|
|
368
|
+
are reported and kept here and there.
|
|
369
|
+
"""
|
|
370
|
+
_run_single("cc stamps check", lambda: mutate.probe_section("stamps"), as_json)
|