bingo-light 2.1.1 → 2.1.3

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
@@ -234,6 +234,7 @@ def _format_conflict_analyze(result: dict) -> str:
234
234
  conflicts = result.get("conflicts", [])
235
235
  if not conflicts:
236
236
  return f"{_c(GREEN, 'OK')} No conflicts detected."
237
+
237
238
  lines = [f"{_c(YELLOW, '!')} {len(conflicts)} conflicting file(s):"]
238
239
  for c in conflicts:
239
240
  f = c.get("file", "?")
@@ -241,6 +242,33 @@ def _format_conflict_analyze(result: dict) -> str:
241
242
  lines.append(f" {_c(RED, f)}")
242
243
  if hint:
243
244
  lines.append(f" hint: {hint}")
245
+
246
+ intent = result.get("patch_intent")
247
+ if intent and intent.get("name"):
248
+ lines.append("")
249
+ lines.append(
250
+ f"{_c(BOLD, 'Patch intent:')} "
251
+ f"{intent['name']} \u2014 {intent.get('subject', '')}"
252
+ )
253
+ meta = intent.get("meta") or {}
254
+ if meta.get("reason"):
255
+ lines.append(f" reason: {meta['reason']}")
256
+ if meta.get("tags"):
257
+ lines.append(f" tags: {', '.join(meta['tags'])}")
258
+ sp = intent.get("stack_position")
259
+ if sp:
260
+ lines.append(f" position: {sp['index']}/{sp['total']}")
261
+
262
+ verify = result.get("verify")
263
+ if verify:
264
+ lines.append("")
265
+ lines.append(f"{_c(BOLD, 'Verify suggestions:')}")
266
+ tc = verify.get("test_command")
267
+ if tc:
268
+ lines.append(f" test.command: {tc}")
269
+ for h in verify.get("file_hints", []):
270
+ lines.append(f" [{h['kind']}] {h['command']}")
271
+
244
272
  return "\n".join(lines)
245
273
 
246
274
 
@@ -261,10 +289,18 @@ def _format_conflict_resolve(result: dict) -> str:
261
289
  )
262
290
 
263
291
  if result.get("sync_complete"):
264
- return (
265
- f"{_c(GREEN, 'OK')} Resolved {resolved} "
266
- f"\u2014 sync complete!"
267
- )
292
+ out = f"{_c(GREEN, 'OK')} Resolved {resolved} \u2014 sync complete!"
293
+ vr = result.get("verify_result")
294
+ if vr:
295
+ if vr.get("skipped"):
296
+ out += f"\n verify: skipped ({vr.get('reason', '')})"
297
+ elif vr.get("test") == "pass":
298
+ out += f"\n {_c(GREEN, 'verify: PASS')} ({vr.get('command', '')})"
299
+ elif vr.get("test") == "fail":
300
+ out += f"\n {_c(RED, 'verify: FAIL')} ({vr.get('command', '')})"
301
+ else:
302
+ out += f"\n verify: {vr.get('test', '?')}"
303
+ return out
268
304
 
269
305
  if result.get("conflict"):
270
306
  patch = result.get("current_patch", "")
@@ -284,10 +320,18 @@ def _format_conflict_resolve(result: dict) -> str:
284
320
  return "\n".join(lines)
285
321
 
286
322
  if result.get("rebase_continued"):
287
- return (
288
- f"{_c(GREEN, 'OK')} Resolved {resolved} "
289
- f"\u2014 rebase continued."
290
- )
323
+ out = f"{_c(GREEN, 'OK')} Resolved {resolved} \u2014 rebase continued."
324
+ vr = result.get("verify_result")
325
+ if vr:
326
+ if vr.get("skipped"):
327
+ out += f"\n verify: skipped ({vr.get('reason', '')})"
328
+ elif vr.get("test") == "pass":
329
+ out += f"\n {_c(GREEN, 'verify: PASS')} ({vr.get('command', '')})"
330
+ elif vr.get("test") == "fail":
331
+ out += f"\n {_c(RED, 'verify: FAIL')} ({vr.get('command', '')})"
332
+ else:
333
+ out += f"\n verify: {vr.get('test', '?')}"
334
+ return out
291
335
 
292
336
  return f"{_c(GREEN, 'OK')} Resolved {resolved}."
293
337
 
@@ -500,6 +544,118 @@ def _format_patch_meta(result: dict) -> str:
500
544
  return f" {k}: {v}" if k else f"{_c(GREEN, 'OK')} Done."
501
545
 
502
546
 
547
+ def _format_patch_lock(result: dict) -> str:
548
+ """Format patch lock/unlock result."""
549
+ if result.get("ok") is False:
550
+ return f"{_c(RED, 'x')} {result.get('error', 'Failed.')}"
551
+ patch = result.get("patch", "")
552
+ owner = result.get("owner", "")
553
+ if result.get("was_locked") is False:
554
+ return f"{_c(GREEN, 'OK')} {patch} was not locked."
555
+ if "locked_at" in result:
556
+ reason = result.get("reason", "")
557
+ msg = f"{_c(GREEN, 'OK')} Locked {_c(BOLD, patch)} by {owner}"
558
+ if reason:
559
+ msg += f" ({reason})"
560
+ return msg
561
+ prev = result.get("previous_owner", "")
562
+ if prev and prev != owner:
563
+ return f"{_c(GREEN, 'OK')} Unlocked {_c(BOLD, patch)} (was locked by {prev})"
564
+ return f"{_c(GREEN, 'OK')} Unlocked {_c(BOLD, patch)}"
565
+
566
+
567
+ def _format_patch_check(result: dict) -> str:
568
+ """Format patch check (obsolescence detection) result."""
569
+ if result.get("ok") is False:
570
+ return f"{_c(RED, 'x')} {result.get('error', 'Failed.')}"
571
+ patches = result.get("patches", [])
572
+ if not patches:
573
+ return "No patches to check."
574
+ lines = []
575
+ for p in patches:
576
+ status = p.get("status", "unknown")
577
+ name = p.get("name", "?")
578
+ reason = p.get("reason", "")
579
+ if status == "obsolete":
580
+ lines.append(f" {_c(YELLOW, 'OBSOLETE')} {_c(BOLD, name)} — {reason}")
581
+ elif status == "active":
582
+ lines.append(f" {_c(GREEN, 'ACTIVE')} {_c(BOLD, name)} — {reason}")
583
+ else:
584
+ lines.append(f" {_c(DIM, 'UNKNOWN')} {name} — {reason}")
585
+ header = f"Checked {len(patches)} patch(es):"
586
+ return header + "\n" + "\n".join(lines)
587
+
588
+
589
+ def _format_patch_upstream(result: dict) -> str:
590
+ """Format patch upstream (PR-ready export) result."""
591
+ if result.get("ok") is False:
592
+ return f"{_c(RED, 'x')} {result.get('error', 'Failed.')}"
593
+ name = result.get("patch", "")
594
+ desc = result.get("description", "")
595
+ files = result.get("files", [])
596
+ stats = result.get("stats", "")
597
+ diff = result.get("diff", "")
598
+ lines = [
599
+ f"{_c(BOLD, 'PR-ready export:')} {name}",
600
+ f" Description: {desc}",
601
+ f" Files: {', '.join(files)}",
602
+ ]
603
+ if stats:
604
+ lines.append(f" Stats: {stats}")
605
+ lines.append("")
606
+ lines.append(diff)
607
+ return "\n".join(lines)
608
+
609
+
610
+ def _format_patch_expire(result: dict) -> str:
611
+ """Format patch expire result."""
612
+ if result.get("ok") is False:
613
+ return f"{_c(RED, 'x')} {result.get('error', 'Failed.')}"
614
+ expired = result.get("expired", [])
615
+ expiring = result.get("expiring_soon", [])
616
+ if not expired and not expiring:
617
+ return f"{_c(GREEN, 'OK')} No expired or expiring patches."
618
+ lines = []
619
+ if expired:
620
+ lines.append(f"{_c(RED, 'Expired')} ({len(expired)}):")
621
+ for e in expired:
622
+ lines.append(f" {_c(RED, 'x')} {_c(BOLD, e['name'])} — expired {e['expires']} ({abs(e['days_left'])}d ago)")
623
+ if expiring:
624
+ lines.append(f"{_c(YELLOW, 'Expiring soon')} ({len(expiring)}):")
625
+ for e in expiring:
626
+ lines.append(f" {_c(YELLOW, '!')} {_c(BOLD, e['name'])} — expires {e['expires']} ({e['days_left']}d left)")
627
+ return "\n".join(lines)
628
+
629
+
630
+ def _format_patch_stats(result: dict) -> str:
631
+ """Format patch stats result."""
632
+ if result.get("ok") is False:
633
+ return f"{_c(RED, 'x')} {result.get('error', 'Failed.')}"
634
+ patches = result.get("patches", [])
635
+ if not patches:
636
+ return "No patches."
637
+ lines = [f"{'#':>3} {'Name':<20} {'Age':>6} {'Size':>10} {'Syncs':>6} {'Status':<12} {'Owner':<15}"]
638
+ lines.append("─" * 80)
639
+ for i, p in enumerate(patches, 1):
640
+ age = f"{p['age_days']}d" if p.get("age_days", -1) >= 0 else "?"
641
+ size = f"+{p.get('insertions', 0)}/-{p.get('deletions', 0)}"
642
+ syncs = str(p.get("syncs_survived", 0))
643
+ status = p.get("status", "")
644
+ owner = p.get("owner", "") or p.get("locked_by", "") or ""
645
+ lines.append(f"{i:>3} {p['name']:<20} {age:>6} {size:>10} {syncs:>6} {status:<12} {owner:<15}")
646
+ return "\n".join(lines)
647
+
648
+
649
+ def _format_report(result: dict) -> str:
650
+ """Format fork health report."""
651
+ if result.get("ok") is False:
652
+ return f"{_c(RED, 'x')} {result.get('error', 'Failed.')}"
653
+ report = result.get("report", "")
654
+ if report:
655
+ return report
656
+ return f"{_c(GREEN, 'OK')} Report generated."
657
+
658
+
503
659
  def _format_undo(result: dict) -> str:
504
660
  """Format undo result."""
505
661
  if result.get("ok") is False:
@@ -517,6 +673,72 @@ def _format_setup(result: dict) -> str:
517
673
  return ""
518
674
 
519
675
 
676
+ def _format_dep(result: dict) -> str:
677
+ """Format dep subcommand results."""
678
+ if result.get("ok") is False:
679
+ return f"{_c(RED, 'x')} {result.get('error', 'Failed')}"
680
+
681
+ lines = []
682
+
683
+ # dep patch result
684
+ if "patch" in result and "package" in result:
685
+ lines.append(f"{_c(GREEN, '✓')} {result['package']}@{result.get('version', '?')} "
686
+ f"→ {result['patch']} ({result.get('files_changed', 0)} file(s))")
687
+ return "\n".join(lines)
688
+
689
+ # dep status
690
+ if "packages" in result and "total_patches" in result:
691
+ pkgs = result["packages"]
692
+ if not pkgs:
693
+ return f"{_c(DIM, '⊘')} No dependency patches tracked"
694
+ lines.append(f" {_c(BOLD, 'Dependency Patches:')}")
695
+ lines.append("")
696
+ for p in pkgs:
697
+ status_color = GREEN if p["status"] == "ok" else YELLOW
698
+ icon = "✓" if p["status"] == "ok" else "!" if p["status"] == "version_mismatch" else "?"
699
+ lines.append(f" {_c(status_color, icon)} {p['package']} "
700
+ f"{_c(DIM, p['manager'])} "
701
+ f"{p['patched_version']}"
702
+ f"{' → ' + p['installed_version'] if p['status'] == 'version_mismatch' else ''} "
703
+ f"({p['patches']} patch(es))")
704
+ lines.append("")
705
+ return "\n".join(lines)
706
+
707
+ # dep list
708
+ if "patches" in result and isinstance(result["patches"], list):
709
+ patches = result["patches"]
710
+ if not patches:
711
+ return f"{_c(DIM, '⊘')} No patches"
712
+ for p in patches:
713
+ icon = _c(GREEN, "✓") if p.get("exists", True) else _c(RED, "✗")
714
+ desc = f" {_c(DIM, p['description'])}" if p.get("description") else ""
715
+ lines.append(f" {icon} {p['package']}/{p['patch']} {_c(DIM, 'v' + p.get('version', '?'))}{desc}")
716
+ return "\n".join(lines)
717
+
718
+ # dep apply / dep sync
719
+ if "applied" in result or "results" in result:
720
+ applied = result.get("applied", 0)
721
+ failed = result.get("failed", 0)
722
+ if isinstance(applied, int):
723
+ lines.append(f"{_c(GREEN, '✓')} {applied} applied, {failed} failed")
724
+ # Show sync conflicts
725
+ for r in result.get("results", []):
726
+ pkg = r.get("package", "?")
727
+ status = r.get("status", "?")
728
+ if status == "conflict":
729
+ for c in r.get("conflicts", []):
730
+ lines.append(f" {_c(YELLOW, '!')} {pkg}/{c['patch']}: {c.get('error', '?')}")
731
+ if c.get("hint"):
732
+ lines.append(f" {_c(DIM, c['hint'])}")
733
+ return "\n".join(lines)
734
+
735
+ # dep drop
736
+ if "dropped" in result:
737
+ return f"{_c(GREEN, '✓')} Dropped {result.get('package', '?')}/{result['dropped']}"
738
+
739
+ return f"{_c(GREEN, '✓')} Done"
740
+
741
+
520
742
  def _format_generic(result: dict) -> str:
521
743
  """Fallback formatter: show message or OK."""
522
744
  if result.get("ok") is False:
@@ -582,6 +804,14 @@ def show_help() -> str:
582
804
  {cy}config{r} get|set|list Manage configuration
583
805
  {cy}test{r} Run configured test suite
584
806
 
807
+ {b}Dependency Patching:{r}
808
+ {cy}dep patch{r} <package> [name] Patch a modified npm/pip dependency
809
+ {cy}dep apply{r} [package] Re-apply patches after install
810
+ {cy}dep sync{r} Re-apply patches after update, detect conflicts
811
+ {cy}dep status{r} Show patch health for all dependencies
812
+ {cy}dep list{r} List all dependency patches
813
+ {cy}dep drop{r} <package> [patch] Remove a dependency patch
814
+
585
815
  {b}Automation:{r}
586
816
  {cy}auto-sync{r} Generate GitHub Actions workflow
587
817
 
@@ -666,7 +896,11 @@ def build_parser() -> argparse.ArgumentParser:
666
896
  sub.add_parser("diff", aliases=["d"], add_help=False)
667
897
 
668
898
  # doctor
669
- sub.add_parser("doctor", add_help=False)
899
+ p_doctor = sub.add_parser("doctor", add_help=False)
900
+ p_doctor.add_argument("--report", action="store_true")
901
+
902
+ # report
903
+ sub.add_parser("report", add_help=False)
670
904
 
671
905
  # log
672
906
  sub.add_parser("log", add_help=False)
@@ -681,6 +915,8 @@ def build_parser() -> argparse.ArgumentParser:
681
915
  cr = sub.add_parser("conflict-resolve", add_help=False)
682
916
  cr.add_argument("resolve_file", nargs="?", default="")
683
917
  cr.add_argument("--content-stdin", action="store_true")
918
+ cr.add_argument("--verify", action="store_true",
919
+ help="Run test.command after the final rebase continues")
684
920
 
685
921
  # session
686
922
  p_session = sub.add_parser("session", add_help=False)
@@ -737,6 +973,23 @@ def build_parser() -> argparse.ArgumentParser:
737
973
  pp_meta.add_argument("meta_key", nargs="?", default="")
738
974
  pp_meta.add_argument("meta_value", nargs="?", default="")
739
975
 
976
+ pp_lock = patch_sub.add_parser("lock", add_help=False)
977
+ pp_lock.add_argument("target")
978
+ pp_lock.add_argument("--reason", default="")
979
+
980
+ pp_unlock = patch_sub.add_parser("unlock", add_help=False)
981
+ pp_unlock.add_argument("target")
982
+ pp_unlock.add_argument("--force", action="store_true")
983
+
984
+ pp_check = patch_sub.add_parser("check", add_help=False)
985
+ pp_check.add_argument("target", nargs="?", default="")
986
+
987
+ pp_upstream = patch_sub.add_parser("upstream", add_help=False)
988
+ pp_upstream.add_argument("target")
989
+
990
+ patch_sub.add_parser("expire", add_help=False)
991
+ patch_sub.add_parser("stats", add_help=False)
992
+
740
993
  # workspace
741
994
  p_ws = sub.add_parser("workspace", aliases=["ws"], add_help=False)
742
995
  ws_sub = p_ws.add_subparsers(dest="ws_command")
@@ -758,6 +1011,47 @@ def build_parser() -> argparse.ArgumentParser:
758
1011
  p_setup = sub.add_parser("setup", add_help=False)
759
1012
  p_setup.add_argument("--no-completions", dest="no_completions", action="store_true")
760
1013
 
1014
+ # dep (dependency patching)
1015
+ p_dep = sub.add_parser("dep", add_help=False)
1016
+ dep_sub = p_dep.add_subparsers(dest="dep_command")
1017
+
1018
+ dp_patch = dep_sub.add_parser("patch", add_help=False)
1019
+ dp_patch.add_argument("dep_package")
1020
+ dp_patch.add_argument("dep_patch_name", nargs="?", default="")
1021
+
1022
+ dp_apply = dep_sub.add_parser("apply", add_help=False)
1023
+ dp_apply.add_argument("dep_package", nargs="?", default="")
1024
+
1025
+ dep_sub.add_parser("list", add_help=False)
1026
+
1027
+ dep_sub.add_parser("status", add_help=False)
1028
+
1029
+ dep_sub.add_parser("sync", add_help=False)
1030
+
1031
+ dp_drop = dep_sub.add_parser("drop", add_help=False)
1032
+ dp_drop.add_argument("dep_package")
1033
+ dp_drop.add_argument("dep_patch_name", nargs="?", default="")
1034
+
1035
+ # dep override subcommands
1036
+ dp_override = dep_sub.add_parser("override", add_help=False)
1037
+ override_sub = dp_override.add_subparsers(dest="override_command")
1038
+ override_sub.add_parser("list", add_help=False)
1039
+ override_sub.add_parser("check", add_help=False)
1040
+ ov_add = override_sub.add_parser("add", add_help=False)
1041
+ ov_add.add_argument("override_package")
1042
+ ov_add.add_argument("override_version")
1043
+ ov_add.add_argument("--reason", dest="override_reason", default="")
1044
+ ov_drop = override_sub.add_parser("drop", add_help=False)
1045
+ ov_drop.add_argument("override_package")
1046
+
1047
+ # dep fork subcommands
1048
+ dp_fork = dep_sub.add_parser("fork", add_help=False)
1049
+ fork_sub = dp_fork.add_subparsers(dest="fork_command")
1050
+ fork_sub.add_parser("list", add_help=False)
1051
+ fork_sub.add_parser("check", add_help=False)
1052
+ fk_sync = fork_sub.add_parser("sync", add_help=False)
1053
+ fk_sync.add_argument("fork_package")
1054
+
761
1055
  # help
762
1056
  sub.add_parser("help", add_help=False)
763
1057
 
@@ -771,6 +1065,51 @@ def dispatch(args: argparse.Namespace, json_mode: bool) -> Optional[dict]:
771
1065
  """Dispatch a parsed command to the Repo method and return the result dict."""
772
1066
  cmd = args.command
773
1067
 
1068
+ # dep doesn't need a Repo (works with package managers, not git)
1069
+ if cmd == "dep":
1070
+ from bingo_core.dep import DepManager
1071
+ dm = DepManager()
1072
+ dcmd = getattr(args, "dep_command", "")
1073
+ if dcmd == "patch":
1074
+ desc = os.environ.get("BINGO_DESCRIPTION", "")
1075
+ return dm.patch(args.dep_package, args.dep_patch_name, desc)
1076
+ if dcmd == "apply":
1077
+ return dm.apply(getattr(args, "dep_package", "") or "")
1078
+ if dcmd == "list":
1079
+ return dm.list_patches()
1080
+ if dcmd == "status":
1081
+ return dm.status()
1082
+ if dcmd == "sync":
1083
+ return dm.sync()
1084
+ if dcmd == "drop":
1085
+ return dm.drop(args.dep_package, getattr(args, "dep_patch_name", ""))
1086
+ if dcmd == "override":
1087
+ ocmd = getattr(args, "override_command", "")
1088
+ if ocmd == "list":
1089
+ return dm.override_list()
1090
+ if ocmd == "check":
1091
+ return dm.override_check()
1092
+ if ocmd == "add":
1093
+ return dm.override_add(
1094
+ args.override_package, args.override_version,
1095
+ getattr(args, "override_reason", ""),
1096
+ )
1097
+ if ocmd == "drop":
1098
+ return dm.override_drop(args.override_package)
1099
+ return {"ok": False, "error": f"Unknown override subcommand: {ocmd}"}
1100
+ if dcmd == "fork":
1101
+ from bingo_core.dep_fork import ForkTracker
1102
+ ft = ForkTracker()
1103
+ fcmd = getattr(args, "fork_command", "")
1104
+ if fcmd == "list":
1105
+ return ft.fork_list()
1106
+ if fcmd == "check":
1107
+ return ft.fork_check()
1108
+ if fcmd == "sync":
1109
+ return ft.fork_sync(args.fork_package)
1110
+ return {"ok": False, "error": f"Unknown fork subcommand: {fcmd}"}
1111
+ return {"ok": False, "error": f"Unknown dep subcommand: {dcmd}"}
1112
+
774
1113
  # setup doesn't need a Repo (works outside git repos)
775
1114
  if cmd == "setup":
776
1115
  return run_setup(
@@ -804,7 +1143,10 @@ def dispatch(args: argparse.Namespace, json_mode: bool) -> Optional[dict]:
804
1143
  return repo.diff()
805
1144
 
806
1145
  if cmd == "doctor":
807
- return repo.doctor()
1146
+ return repo.doctor(report=getattr(args, "report", False))
1147
+
1148
+ if cmd == "report":
1149
+ return repo.report()
808
1150
 
809
1151
  if cmd == "log":
810
1152
  return repo.history()
@@ -820,7 +1162,9 @@ def dispatch(args: argparse.Namespace, json_mode: bool) -> Optional[dict]:
820
1162
  if args.content_stdin:
821
1163
  import sys as _sys
822
1164
  content = _sys.stdin.read()
823
- return repo.conflict_resolve(args.resolve_file, content)
1165
+ return repo.conflict_resolve(
1166
+ args.resolve_file, content, verify=args.verify
1167
+ )
824
1168
 
825
1169
  if cmd == "session":
826
1170
  update = (args.session_action == "update")
@@ -903,6 +1247,24 @@ def _dispatch_patch(args: argparse.Namespace, repo: Repo) -> dict:
903
1247
  value=meta_value,
904
1248
  )
905
1249
 
1250
+ if pcmd == "lock":
1251
+ return repo.patch_lock(args.target, reason=getattr(args, "reason", ""))
1252
+
1253
+ if pcmd == "unlock":
1254
+ return repo.patch_unlock(args.target, force=getattr(args, "force", False))
1255
+
1256
+ if pcmd == "check":
1257
+ return repo.patch_check(getattr(args, "target", ""))
1258
+
1259
+ if pcmd == "upstream":
1260
+ return repo.patch_upstream(args.target)
1261
+
1262
+ if pcmd == "expire":
1263
+ return repo.patch_expire()
1264
+
1265
+ if pcmd == "stats":
1266
+ return repo.patch_stats()
1267
+
906
1268
  raise BingoError(f"Unknown patch subcommand: {pcmd}")
907
1269
 
908
1270
 
@@ -959,6 +1321,8 @@ _FORMATTERS: Dict[str, Any] = {
959
1321
  "undo": _format_undo,
960
1322
  "smart-sync": _format_smart_sync,
961
1323
  "setup": _format_setup,
1324
+ "dep": _format_dep,
1325
+ "report": _format_report,
962
1326
  }
963
1327
 
964
1328
 
@@ -983,6 +1347,16 @@ def _get_formatter(args: argparse.Namespace):
983
1347
  return _format_patch_import
984
1348
  if pcmd == "meta":
985
1349
  return _format_patch_meta
1350
+ if pcmd in ("lock", "unlock"):
1351
+ return _format_patch_lock
1352
+ if pcmd == "check":
1353
+ return _format_patch_check
1354
+ if pcmd == "upstream":
1355
+ return _format_patch_upstream
1356
+ if pcmd == "expire":
1357
+ return _format_patch_expire
1358
+ if pcmd == "stats":
1359
+ return _format_patch_stats
986
1360
  return _format_generic
987
1361
 
988
1362
  # workspace subcommands
@@ -14,7 +14,7 @@ import re
14
14
 
15
15
  # --- Constants ---
16
16
 
17
- VERSION = "2.1.1"
17
+ VERSION = "2.1.3"
18
18
  PATCH_PREFIX = "[bl]"
19
19
  CONFIG_FILE = ".bingolight"
20
20
  BINGO_DIR = ".bingo"
@@ -42,6 +42,7 @@ from bingo_core.models import PatchInfo, ConflictInfo # noqa: E402
42
42
  from bingo_core.git import Git # noqa: E402
43
43
  from bingo_core.config import Config # noqa: E402
44
44
  from bingo_core.state import State # noqa: E402
45
+ from bingo_core.team import TeamState # noqa: E402
45
46
  from bingo_core.repo import Repo # noqa: E402
46
47
 
47
48
  __all__ = [
@@ -73,5 +74,6 @@ __all__ = [
73
74
  "Git",
74
75
  "Config",
75
76
  "State",
77
+ "TeamState",
76
78
  "Repo",
77
79
  ]