|
26 | 26 | # @cpt-begin:cpt-cypilot-flow-version-config-update:p1:inst-update-imports |
27 | 27 | import argparse |
28 | 28 | import json |
29 | | -import re |
30 | 29 | import shutil |
31 | 30 | import sys |
32 | 31 | from pathlib import Path |
|
45 | 44 | _inject_root_claude, |
46 | 45 | ) |
47 | 46 | from ..utils.ui import ui |
| 47 | +from ..utils.whatsnew import read_whatsnew, show_core_whatsnew, show_kit_whatsnew |
48 | 48 | # @cpt-end:cpt-cypilot-flow-version-config-update:p1:inst-update-imports |
49 | 49 |
|
50 | 50 | def cmd_update(argv: List[str]) -> int: |
@@ -134,10 +134,10 @@ def cmd_update(argv: List[str]) -> int: |
134 | 134 |
|
135 | 135 | # ── Show core whatsnew (before .core/ is replaced) ──────────────────── |
136 | 136 | if not args.dry_run: |
137 | | - cache_whatsnew = _read_core_whatsnew(CACHE_DIR / "whatsnew.toml") |
138 | | - core_whatsnew = _read_core_whatsnew(core_dir / "whatsnew.toml") |
| 137 | + cache_whatsnew = read_whatsnew(CACHE_DIR / "whatsnew.toml") |
| 138 | + core_whatsnew = read_whatsnew(core_dir / "whatsnew.toml") |
139 | 139 | if cache_whatsnew: |
140 | | - ack = _show_core_whatsnew( |
| 140 | + ack = show_core_whatsnew( |
141 | 141 | cache_whatsnew, core_whatsnew, |
142 | 142 | interactive=not args.no_interactive and not args.yes and sys.stdin.isatty(), |
143 | 143 | ) |
@@ -225,6 +225,7 @@ def cmd_update(argv: List[str]) -> int: |
225 | 225 | from .kit import ( |
226 | 226 | update_kit, regenerate_gen_aggregates, |
227 | 227 | _read_kits_from_core_toml, _parse_github_source, _download_kit_from_github, |
| 228 | + _read_kit_version_from_core, |
228 | 229 | migrate_legacy_kit_to_manifest, |
229 | 230 | ) |
230 | 231 |
|
@@ -258,6 +259,24 @@ def cmd_update(argv: List[str]) -> int: |
258 | 259 | if kit_src is None: |
259 | 260 | continue |
260 | 261 |
|
| 262 | + if not args.dry_run: |
| 263 | + installed_version = _read_kit_version_from_core(config_dir, kit_slug) |
| 264 | + ack = show_kit_whatsnew( |
| 265 | + kit_src, |
| 266 | + installed_version, |
| 267 | + kit_slug, |
| 268 | + interactive=interactive and not args.yes, |
| 269 | + ) |
| 270 | + if not ack: |
| 271 | + kit_r = { |
| 272 | + "kit": kit_slug, |
| 273 | + "version": {"status": "aborted"}, |
| 274 | + "gen": {"files_written": 0}, |
| 275 | + "gen_rejected": [], |
| 276 | + } |
| 277 | + kit_results[kit_slug] = kit_r |
| 278 | + continue |
| 279 | + |
261 | 280 | try: |
262 | 281 | kit_r = update_kit( |
263 | 282 | kit_slug, kit_src, cypilot_dir, |
@@ -337,6 +356,8 @@ def cmd_update(argv: List[str]) -> int: |
337 | 356 | ui.substep(f" ~ {fp}") |
338 | 357 | for fp in rejected: |
339 | 358 | ui.substep(f" ✗ {fp} (declined)") |
| 359 | + elif ver_status == "aborted": |
| 360 | + ui.substep(f"{kit_slug}: skipped by user") |
340 | 361 | elif ver_status == "current": |
341 | 362 | ui.substep(f"{kit_slug}: up to date") |
342 | 363 |
|
@@ -802,89 +823,6 @@ def _migrate_kit_sources(config_dir: Path) -> Dict[str, str]: |
802 | 823 |
|
803 | 824 | # Re-exported from kit.py — tests import it from here |
804 | 825 | from .kit import _read_conf_version as _read_conf_version # noqa: F401 |
805 | | - |
806 | | -def _read_core_whatsnew(path: Path) -> Dict[str, Dict[str, str]]: |
807 | | - """Read a standalone whatsnew.toml file. |
808 | | -
|
809 | | - Returns dict mapping version string to {summary, details}. |
810 | | - """ |
811 | | - if not path.is_file(): |
812 | | - return {} |
813 | | - try: |
814 | | - import tomllib |
815 | | - with open(path, "rb") as f: |
816 | | - data = tomllib.load(f) |
817 | | - except Exception: |
818 | | - return {} |
819 | | - result: Dict[str, Dict[str, str]] = {} |
820 | | - for key, entry in data.items(): |
821 | | - if isinstance(entry, dict): |
822 | | - result[key] = { |
823 | | - "summary": str(entry.get("summary", "")), |
824 | | - "details": str(entry.get("details", "")), |
825 | | - } |
826 | | - return result |
827 | | - |
828 | | - |
829 | | -def _stderr_supports_ansi() -> bool: |
830 | | - return hasattr(sys.stderr, "isatty") and sys.stderr.isatty() |
831 | | - |
832 | | - |
833 | | -def _format_whatsnew_text(text: str, *, use_ansi: bool) -> str: |
834 | | - if use_ansi: |
835 | | - formatted = re.sub(r"\*\*(.+?)\*\*", r"\033[1m\1\033[0m", text) |
836 | | - return re.sub(r"`(.+?)`", r"\033[36m\1\033[0m", formatted) |
837 | | - plain = re.sub(r"\*\*(.+?)\*\*", r"\1", text) |
838 | | - return re.sub(r"`(.+?)`", r"\1", plain) |
839 | | - |
840 | | - |
841 | | -def _show_core_whatsnew( |
842 | | - ref_whatsnew: Dict[str, Dict[str, str]], |
843 | | - core_whatsnew: Dict[str, Dict[str, str]], |
844 | | - *, |
845 | | - interactive: bool = True, |
846 | | -) -> bool: |
847 | | - """Display core whatsnew entries present in cache but missing from .core/. |
848 | | -
|
849 | | - Returns True if user acknowledged (or non-interactive), False if declined. |
850 | | - """ |
851 | | - missing = sorted( |
852 | | - (v, ref_whatsnew[v]) for v in ref_whatsnew |
853 | | - if v not in core_whatsnew |
854 | | - ) |
855 | | - if not missing: |
856 | | - return True |
857 | | - |
858 | | - sys.stderr.write(f"\n{'=' * 60}\n") |
859 | | - sys.stderr.write(f" What's new in Cypilot\n") |
860 | | - sys.stderr.write(f"{'=' * 60}\n") |
861 | | - |
862 | | - use_ansi = _stderr_supports_ansi() |
863 | | - for ver, entry in missing: |
864 | | - summary = _format_whatsnew_text(entry["summary"], use_ansi=use_ansi) |
865 | | - if use_ansi and summary == entry["summary"]: |
866 | | - sys.stderr.write(f"\n \033[1m{ver}: {entry['summary']}\033[0m\n") |
867 | | - else: |
868 | | - version_label = f"\033[1m{ver}:\033[0m" if use_ansi else f"{ver}:" |
869 | | - sys.stderr.write(f"\n {version_label} {summary}\n") |
870 | | - if entry["details"]: |
871 | | - for line in entry["details"].splitlines(): |
872 | | - sys.stderr.write( |
873 | | - f" {_format_whatsnew_text(line, use_ansi=use_ansi)}\n" |
874 | | - ) |
875 | | - |
876 | | - sys.stderr.write(f"\n{'=' * 60}\n") |
877 | | - |
878 | | - if not interactive: |
879 | | - return True |
880 | | - |
881 | | - sys.stderr.write(" Press Enter to continue with update (or 'q' to abort): ") |
882 | | - sys.stderr.flush() |
883 | | - try: |
884 | | - response = input().strip().lower() |
885 | | - except EOFError: |
886 | | - return False |
887 | | - return response != "q" |
888 | 826 | # @cpt-end:cpt-cypilot-flow-version-config-update:p1:inst-update-helpers |
889 | 827 |
|
890 | 828 | # --------------------------------------------------------------------------- |
|
0 commit comments