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/README.en.md +20 -7
- package/README.md +209 -126
- package/bingo-light +385 -11
- package/bingo_core/__init__.py +3 -1
- package/bingo_core/dep.py +1012 -0
- package/bingo_core/dep_fork.py +268 -0
- package/bingo_core/dep_npm.py +113 -0
- package/bingo_core/dep_pip.py +178 -0
- package/bingo_core/repo.py +795 -8
- package/bingo_core/setup.py +73 -17
- package/bingo_core/state.py +1 -1
- package/bingo_core/team.py +170 -0
- package/completions/bingo-light.bash +26 -3
- package/completions/bingo-light.fish +46 -1
- package/completions/bingo-light.zsh +38 -2
- package/mcp-server.py +346 -6
- package/package.json +1 -1
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
|
-
|
|
265
|
-
|
|
266
|
-
|
|
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
|
-
|
|
288
|
-
|
|
289
|
-
|
|
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(
|
|
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
|
package/bingo_core/__init__.py
CHANGED
|
@@ -14,7 +14,7 @@ import re
|
|
|
14
14
|
|
|
15
15
|
# --- Constants ---
|
|
16
16
|
|
|
17
|
-
VERSION = "2.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
|
]
|