bingo-light 2.1.0 → 2.1.2

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.
package/bingo-light CHANGED
@@ -517,6 +517,72 @@ def _format_setup(result: dict) -> str:
517
517
  return ""
518
518
 
519
519
 
520
+ def _format_dep(result: dict) -> str:
521
+ """Format dep subcommand results."""
522
+ if result.get("ok") is False:
523
+ return f"{_c(RED, 'x')} {result.get('error', 'Failed')}"
524
+
525
+ lines = []
526
+
527
+ # dep patch result
528
+ if "patch" in result and "package" in result:
529
+ lines.append(f"{_c(GREEN, '✓')} {result['package']}@{result.get('version', '?')} "
530
+ f"→ {result['patch']} ({result.get('files_changed', 0)} file(s))")
531
+ return "\n".join(lines)
532
+
533
+ # dep status
534
+ if "packages" in result and "total_patches" in result:
535
+ pkgs = result["packages"]
536
+ if not pkgs:
537
+ return f"{_c(DIM, '⊘')} No dependency patches tracked"
538
+ lines.append(f" {_c(BOLD, 'Dependency Patches:')}")
539
+ lines.append("")
540
+ for p in pkgs:
541
+ status_color = GREEN if p["status"] == "ok" else YELLOW
542
+ icon = "✓" if p["status"] == "ok" else "!" if p["status"] == "version_mismatch" else "?"
543
+ lines.append(f" {_c(status_color, icon)} {p['package']} "
544
+ f"{_c(DIM, p['manager'])} "
545
+ f"{p['patched_version']}"
546
+ f"{' → ' + p['installed_version'] if p['status'] == 'version_mismatch' else ''} "
547
+ f"({p['patches']} patch(es))")
548
+ lines.append("")
549
+ return "\n".join(lines)
550
+
551
+ # dep list
552
+ if "patches" in result and isinstance(result["patches"], list):
553
+ patches = result["patches"]
554
+ if not patches:
555
+ return f"{_c(DIM, '⊘')} No patches"
556
+ for p in patches:
557
+ icon = _c(GREEN, "✓") if p.get("exists", True) else _c(RED, "✗")
558
+ desc = f" {_c(DIM, p['description'])}" if p.get("description") else ""
559
+ lines.append(f" {icon} {p['package']}/{p['patch']} {_c(DIM, 'v' + p.get('version', '?'))}{desc}")
560
+ return "\n".join(lines)
561
+
562
+ # dep apply / dep sync
563
+ if "applied" in result or "results" in result:
564
+ applied = result.get("applied", 0)
565
+ failed = result.get("failed", 0)
566
+ if isinstance(applied, int):
567
+ lines.append(f"{_c(GREEN, '✓')} {applied} applied, {failed} failed")
568
+ # Show sync conflicts
569
+ for r in result.get("results", []):
570
+ pkg = r.get("package", "?")
571
+ status = r.get("status", "?")
572
+ if status == "conflict":
573
+ for c in r.get("conflicts", []):
574
+ lines.append(f" {_c(YELLOW, '!')} {pkg}/{c['patch']}: {c.get('error', '?')}")
575
+ if c.get("hint"):
576
+ lines.append(f" {_c(DIM, c['hint'])}")
577
+ return "\n".join(lines)
578
+
579
+ # dep drop
580
+ if "dropped" in result:
581
+ return f"{_c(GREEN, '✓')} Dropped {result.get('package', '?')}/{result['dropped']}"
582
+
583
+ return f"{_c(GREEN, '✓')} Done"
584
+
585
+
520
586
  def _format_generic(result: dict) -> str:
521
587
  """Fallback formatter: show message or OK."""
522
588
  if result.get("ok") is False:
@@ -582,6 +648,14 @@ def show_help() -> str:
582
648
  {cy}config{r} get|set|list Manage configuration
583
649
  {cy}test{r} Run configured test suite
584
650
 
651
+ {b}Dependency Patching:{r}
652
+ {cy}dep patch{r} <package> [name] Patch a modified npm/pip dependency
653
+ {cy}dep apply{r} [package] Re-apply patches after install
654
+ {cy}dep sync{r} Re-apply patches after update, detect conflicts
655
+ {cy}dep status{r} Show patch health for all dependencies
656
+ {cy}dep list{r} List all dependency patches
657
+ {cy}dep drop{r} <package> [patch] Remove a dependency patch
658
+
585
659
  {b}Automation:{r}
586
660
  {cy}auto-sync{r} Generate GitHub Actions workflow
587
661
 
@@ -758,6 +832,27 @@ def build_parser() -> argparse.ArgumentParser:
758
832
  p_setup = sub.add_parser("setup", add_help=False)
759
833
  p_setup.add_argument("--no-completions", dest="no_completions", action="store_true")
760
834
 
835
+ # dep (dependency patching)
836
+ p_dep = sub.add_parser("dep", add_help=False)
837
+ dep_sub = p_dep.add_subparsers(dest="dep_command")
838
+
839
+ dp_patch = dep_sub.add_parser("patch", add_help=False)
840
+ dp_patch.add_argument("dep_package")
841
+ dp_patch.add_argument("dep_patch_name", nargs="?", default="")
842
+
843
+ dp_apply = dep_sub.add_parser("apply", add_help=False)
844
+ dp_apply.add_argument("dep_package", nargs="?", default="")
845
+
846
+ dep_sub.add_parser("list", add_help=False)
847
+
848
+ dep_sub.add_parser("status", add_help=False)
849
+
850
+ dep_sub.add_parser("sync", add_help=False)
851
+
852
+ dp_drop = dep_sub.add_parser("drop", add_help=False)
853
+ dp_drop.add_argument("dep_package")
854
+ dp_drop.add_argument("dep_patch_name", nargs="?", default="")
855
+
761
856
  # help
762
857
  sub.add_parser("help", add_help=False)
763
858
 
@@ -771,6 +866,26 @@ def dispatch(args: argparse.Namespace, json_mode: bool) -> Optional[dict]:
771
866
  """Dispatch a parsed command to the Repo method and return the result dict."""
772
867
  cmd = args.command
773
868
 
869
+ # dep doesn't need a Repo (works with package managers, not git)
870
+ if cmd == "dep":
871
+ from bingo_core.dep import DepManager
872
+ dm = DepManager()
873
+ dcmd = getattr(args, "dep_command", "")
874
+ if dcmd == "patch":
875
+ desc = os.environ.get("BINGO_DESCRIPTION", "")
876
+ return dm.patch(args.dep_package, args.dep_patch_name, desc)
877
+ if dcmd == "apply":
878
+ return dm.apply(getattr(args, "dep_package", "") or "")
879
+ if dcmd == "list":
880
+ return dm.list_patches()
881
+ if dcmd == "status":
882
+ return dm.status()
883
+ if dcmd == "sync":
884
+ return dm.sync()
885
+ if dcmd == "drop":
886
+ return dm.drop(args.dep_package, getattr(args, "dep_patch_name", ""))
887
+ return {"ok": False, "error": f"Unknown dep subcommand: {dcmd}"}
888
+
774
889
  # setup doesn't need a Repo (works outside git repos)
775
890
  if cmd == "setup":
776
891
  return run_setup(
@@ -959,6 +1074,7 @@ _FORMATTERS: Dict[str, Any] = {
959
1074
  "undo": _format_undo,
960
1075
  "smart-sync": _format_smart_sync,
961
1076
  "setup": _format_setup,
1077
+ "dep": _format_dep,
962
1078
  }
963
1079
 
964
1080
 
@@ -14,7 +14,7 @@ import re
14
14
 
15
15
  # --- Constants ---
16
16
 
17
- VERSION = "2.1.0"
17
+ VERSION = "2.1.2"
18
18
  PATCH_PREFIX = "[bl]"
19
19
  CONFIG_FILE = ".bingolight"
20
20
  BINGO_DIR = ".bingo"