bingo-light 2.1.2 → 2.2.0
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 +17 -7
- package/README.md +30 -4
- package/bingo-light +321 -11
- package/bingo_core/__init__.py +9 -1
- package/bingo_core/decisions.py +167 -0
- package/bingo_core/dep.py +385 -25
- package/bingo_core/dep_fork.py +268 -0
- package/bingo_core/models.py +1 -0
- package/bingo_core/repo.py +1031 -9
- package/bingo_core/semantic.py +85 -0
- package/bingo_core/state.py +1 -1
- package/bingo_core/team.py +170 -0
- package/completions/bingo-light.bash +14 -4
- package/completions/bingo-light.fish +23 -2
- package/completions/bingo-light.zsh +18 -2
- package/mcp-server.py +245 -7
- package/package.json +1 -1
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""
|
|
2
|
+
bingo_core.semantic — semantic classification of conflict regions.
|
|
3
|
+
|
|
4
|
+
Given ours/theirs text for a single conflict region, return one of:
|
|
5
|
+
"whitespace" — regions differ only in whitespace
|
|
6
|
+
"import_reorder" — both regions are only import statements,
|
|
7
|
+
same set of imports, just reordered
|
|
8
|
+
"signature_change" — function/method signature changed (params
|
|
9
|
+
added, removed, or renamed) but name same
|
|
10
|
+
"logic" — default; real logic change requiring human
|
|
11
|
+
or AI reasoning
|
|
12
|
+
|
|
13
|
+
The classifier is intentionally conservative: when unsure, return
|
|
14
|
+
"logic" so callers treat it as a real conflict.
|
|
15
|
+
|
|
16
|
+
Python 3.8+ stdlib only.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
import re
|
|
22
|
+
|
|
23
|
+
# Matches Python "import X" or "from X import Y" lines.
|
|
24
|
+
_IMPORT_RE_PY = re.compile(r"^\s*(?:import|from)\s+\S")
|
|
25
|
+
# Matches JS/TS imports and CommonJS require.
|
|
26
|
+
_IMPORT_RE_JS = re.compile(r"^\s*(?:import\s|const\s+\w+\s*=\s*require\()")
|
|
27
|
+
|
|
28
|
+
# Function signature captures: (name, params) for Python def and JS function.
|
|
29
|
+
_FN_SIG_PY = re.compile(r"^\s*(?:async\s+)?def\s+(\w+)\s*\(([^)]*)\)")
|
|
30
|
+
_FN_SIG_JS = re.compile(r"^\s*(?:async\s+)?function\s+(\w+)\s*\(([^)]*)\)")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def classify_conflict(ours: str, theirs: str, file: str = "") -> str:
|
|
34
|
+
"""Classify a conflict region as whitespace / import_reorder /
|
|
35
|
+
signature_change / logic.
|
|
36
|
+
|
|
37
|
+
`file` is accepted for future extension (language-specific rules)
|
|
38
|
+
but not currently used.
|
|
39
|
+
"""
|
|
40
|
+
if _is_whitespace_only(ours, theirs):
|
|
41
|
+
return "whitespace"
|
|
42
|
+
if _is_import_reorder(ours, theirs):
|
|
43
|
+
return "import_reorder"
|
|
44
|
+
if _is_signature_change(ours, theirs):
|
|
45
|
+
return "signature_change"
|
|
46
|
+
return "logic"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _is_whitespace_only(a: str, b: str) -> bool:
|
|
50
|
+
"""True if the only difference is whitespace (tabs, spaces, newlines).
|
|
51
|
+
|
|
52
|
+
All whitespace is removed for comparison — matching git's
|
|
53
|
+
`diff --ignore-all-space` semantics.
|
|
54
|
+
"""
|
|
55
|
+
na = "".join(a.split())
|
|
56
|
+
nb = "".join(b.split())
|
|
57
|
+
return na == nb and na != ""
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _is_import_reorder(a: str, b: str) -> bool:
|
|
61
|
+
"""True if both sides contain only import statements, with the same
|
|
62
|
+
set of imports (just reordered)."""
|
|
63
|
+
a_lines = [ln for ln in a.splitlines() if ln.strip()]
|
|
64
|
+
b_lines = [ln for ln in b.splitlines() if ln.strip()]
|
|
65
|
+
if not a_lines or not b_lines:
|
|
66
|
+
return False
|
|
67
|
+
for line in a_lines + b_lines:
|
|
68
|
+
if not (_IMPORT_RE_PY.match(line) or _IMPORT_RE_JS.match(line)):
|
|
69
|
+
return False
|
|
70
|
+
# Compare as sorted sets: order-insensitive match.
|
|
71
|
+
return sorted(a_lines) == sorted(b_lines)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _is_signature_change(a: str, b: str) -> bool:
|
|
75
|
+
"""True if both sides contain a function definition for the same
|
|
76
|
+
name but with different parameter lists."""
|
|
77
|
+
for pattern in (_FN_SIG_PY, _FN_SIG_JS):
|
|
78
|
+
a_match = pattern.search(a)
|
|
79
|
+
b_match = pattern.search(b)
|
|
80
|
+
if a_match and b_match:
|
|
81
|
+
same_name = a_match.group(1) == b_match.group(1)
|
|
82
|
+
different_params = a_match.group(2).strip() != b_match.group(2).strip()
|
|
83
|
+
if same_name and different_params:
|
|
84
|
+
return True
|
|
85
|
+
return False
|
package/bingo_core/state.py
CHANGED
|
@@ -184,7 +184,7 @@ class State:
|
|
|
184
184
|
for t in (v.strip() for v in value.split(",")):
|
|
185
185
|
if t and t not in tags_list:
|
|
186
186
|
tags_list.append(t)
|
|
187
|
-
elif key in ("reason", "expires", "upstream_pr", "status"):
|
|
187
|
+
elif key in ("reason", "expires", "upstream_pr", "status", "owner"):
|
|
188
188
|
p[key] = value
|
|
189
189
|
self._save_metadata(data)
|
|
190
190
|
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
"""
|
|
2
|
+
bingo_core.team — Team collaboration state (.bingo/team.json).
|
|
3
|
+
|
|
4
|
+
Manages patch locks and team membership for multi-person fork maintenance.
|
|
5
|
+
Advisory locking: prevents accidental concurrent edits, not a security boundary.
|
|
6
|
+
|
|
7
|
+
Storage:
|
|
8
|
+
.bingo/team.json
|
|
9
|
+
{
|
|
10
|
+
"locks": {
|
|
11
|
+
"<patch_name>": {
|
|
12
|
+
"owner": "<user>",
|
|
13
|
+
"locked_at": "ISO8601",
|
|
14
|
+
"reason": ""
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
Python 3.8+ stdlib only.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
import json
|
|
25
|
+
import os
|
|
26
|
+
import tempfile
|
|
27
|
+
from datetime import datetime, timezone
|
|
28
|
+
from typing import List, Optional
|
|
29
|
+
|
|
30
|
+
from bingo_core import BINGO_DIR
|
|
31
|
+
from bingo_core.exceptions import BingoError
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class TeamState:
|
|
35
|
+
"""Manages .bingo/team.json for patch locking and team coordination."""
|
|
36
|
+
|
|
37
|
+
def __init__(self, repo_dir: str, git=None):
|
|
38
|
+
self.repo_dir = repo_dir
|
|
39
|
+
self._git = git # optional Git instance for get_user()
|
|
40
|
+
self.bingo_dir = os.path.join(repo_dir, BINGO_DIR)
|
|
41
|
+
self.team_file = os.path.join(self.bingo_dir, "team.json")
|
|
42
|
+
|
|
43
|
+
def _load(self) -> dict:
|
|
44
|
+
"""Load team.json, returning empty structure if missing."""
|
|
45
|
+
if not os.path.isfile(self.team_file):
|
|
46
|
+
return {"locks": {}}
|
|
47
|
+
try:
|
|
48
|
+
with open(self.team_file) as f:
|
|
49
|
+
data = json.load(f)
|
|
50
|
+
if "locks" not in data:
|
|
51
|
+
data["locks"] = {}
|
|
52
|
+
return data
|
|
53
|
+
except (json.JSONDecodeError, IOError):
|
|
54
|
+
return {"locks": {}}
|
|
55
|
+
|
|
56
|
+
def _save(self, data: dict) -> None:
|
|
57
|
+
"""Atomically write team.json."""
|
|
58
|
+
os.makedirs(self.bingo_dir, exist_ok=True)
|
|
59
|
+
dir_name = os.path.dirname(self.team_file)
|
|
60
|
+
fd, tmp_path = tempfile.mkstemp(suffix=".tmp", dir=dir_name)
|
|
61
|
+
try:
|
|
62
|
+
with os.fdopen(fd, "w") as f:
|
|
63
|
+
json.dump(data, f, indent=2)
|
|
64
|
+
os.replace(tmp_path, self.team_file)
|
|
65
|
+
except Exception:
|
|
66
|
+
try:
|
|
67
|
+
os.unlink(tmp_path)
|
|
68
|
+
except FileNotFoundError:
|
|
69
|
+
pass
|
|
70
|
+
raise
|
|
71
|
+
|
|
72
|
+
def get_user(self) -> str:
|
|
73
|
+
"""Detect current user from git config or environment."""
|
|
74
|
+
if self._git:
|
|
75
|
+
for key in ("user.name", "user.email"):
|
|
76
|
+
try:
|
|
77
|
+
val = self._git.run("config", key, check=False)
|
|
78
|
+
if val and val.strip():
|
|
79
|
+
return val.strip()
|
|
80
|
+
except Exception:
|
|
81
|
+
pass
|
|
82
|
+
return os.environ.get("USER", "unknown")
|
|
83
|
+
|
|
84
|
+
def lock(self, patch_name: str, owner: str = "", reason: str = "") -> dict:
|
|
85
|
+
"""Lock a patch for exclusive editing.
|
|
86
|
+
|
|
87
|
+
Returns {"ok": True, "patch": ..., "owner": ..., "locked_at": ...}
|
|
88
|
+
Raises BingoError if already locked by another user.
|
|
89
|
+
"""
|
|
90
|
+
if not owner:
|
|
91
|
+
owner = self.get_user()
|
|
92
|
+
data = self._load()
|
|
93
|
+
existing = data["locks"].get(patch_name)
|
|
94
|
+
if existing and existing["owner"] != owner:
|
|
95
|
+
raise BingoError(
|
|
96
|
+
f"Patch '{patch_name}' is locked by {existing['owner']} "
|
|
97
|
+
f"(since {existing['locked_at']}). "
|
|
98
|
+
f"They must unlock it first, or use --force."
|
|
99
|
+
)
|
|
100
|
+
now = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
101
|
+
data["locks"][patch_name] = {
|
|
102
|
+
"owner": owner,
|
|
103
|
+
"locked_at": now,
|
|
104
|
+
"reason": reason,
|
|
105
|
+
}
|
|
106
|
+
self._save(data)
|
|
107
|
+
return {
|
|
108
|
+
"ok": True,
|
|
109
|
+
"patch": patch_name,
|
|
110
|
+
"owner": owner,
|
|
111
|
+
"locked_at": now,
|
|
112
|
+
"reason": reason,
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
def unlock(self, patch_name: str, owner: str = "", force: bool = False) -> dict:
|
|
116
|
+
"""Unlock a patch.
|
|
117
|
+
|
|
118
|
+
Returns {"ok": True, "patch": ..., "owner": ...}
|
|
119
|
+
Raises BingoError if locked by someone else (unless force=True).
|
|
120
|
+
"""
|
|
121
|
+
if not owner:
|
|
122
|
+
owner = self.get_user()
|
|
123
|
+
data = self._load()
|
|
124
|
+
existing = data["locks"].get(patch_name)
|
|
125
|
+
if not existing:
|
|
126
|
+
return {"ok": True, "patch": patch_name, "owner": owner, "was_locked": False}
|
|
127
|
+
if existing["owner"] != owner and not force:
|
|
128
|
+
raise BingoError(
|
|
129
|
+
f"Patch '{patch_name}' is locked by {existing['owner']}. "
|
|
130
|
+
"Use --force to override."
|
|
131
|
+
)
|
|
132
|
+
del data["locks"][patch_name]
|
|
133
|
+
self._save(data)
|
|
134
|
+
return {
|
|
135
|
+
"ok": True,
|
|
136
|
+
"patch": patch_name,
|
|
137
|
+
"owner": owner,
|
|
138
|
+
"was_locked": True,
|
|
139
|
+
"previous_owner": existing["owner"],
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
def get_lock(self, patch_name: str) -> Optional[dict]:
|
|
143
|
+
"""Get lock info for a patch, or None if unlocked."""
|
|
144
|
+
data = self._load()
|
|
145
|
+
return data["locks"].get(patch_name)
|
|
146
|
+
|
|
147
|
+
def is_locked_by_other(self, patch_name: str, current_user: str = "") -> bool:
|
|
148
|
+
"""Check if a patch is locked by someone other than current_user."""
|
|
149
|
+
if not current_user:
|
|
150
|
+
current_user = self.get_user()
|
|
151
|
+
lock = self.get_lock(patch_name)
|
|
152
|
+
if not lock:
|
|
153
|
+
return False
|
|
154
|
+
return lock["owner"] != current_user
|
|
155
|
+
|
|
156
|
+
def list_locks(self) -> List[dict]:
|
|
157
|
+
"""List all active locks.
|
|
158
|
+
|
|
159
|
+
Returns list of {"patch": ..., "owner": ..., "locked_at": ..., "reason": ...}
|
|
160
|
+
"""
|
|
161
|
+
data = self._load()
|
|
162
|
+
result = []
|
|
163
|
+
for patch_name, info in data["locks"].items():
|
|
164
|
+
result.append({
|
|
165
|
+
"patch": patch_name,
|
|
166
|
+
"owner": info.get("owner", ""),
|
|
167
|
+
"locked_at": info.get("locked_at", ""),
|
|
168
|
+
"reason": info.get("reason", ""),
|
|
169
|
+
})
|
|
170
|
+
return result
|
|
@@ -10,11 +10,11 @@ _bingo_light() {
|
|
|
10
10
|
local cur prev words cword
|
|
11
11
|
_init_completion || return
|
|
12
12
|
|
|
13
|
-
local -r toplevel_commands="init setup patch dep sync status doctor auto-sync log undo diff version help conflict-analyze conflict-resolve config history test workspace smart-sync session"
|
|
13
|
+
local -r toplevel_commands="init setup patch dep sync status doctor auto-sync log undo diff version help conflict-analyze conflict-resolve config history test workspace smart-sync session report"
|
|
14
14
|
local -r toplevel_aliases="p s st d ws"
|
|
15
15
|
local -r all_toplevel="${toplevel_commands} ${toplevel_aliases}"
|
|
16
16
|
|
|
17
|
-
local -r patch_subcommands="new list show edit drop export import reorder squash meta"
|
|
17
|
+
local -r patch_subcommands="new list show edit drop export import reorder squash meta lock unlock check upstream expire stats"
|
|
18
18
|
local -r patch_aliases="ls add create rm remove"
|
|
19
19
|
local -r all_patch="${patch_subcommands} ${patch_aliases}"
|
|
20
20
|
|
|
@@ -39,7 +39,7 @@ _bingo_light() {
|
|
|
39
39
|
dep)
|
|
40
40
|
cmd="dep"
|
|
41
41
|
;;
|
|
42
|
-
init|setup|doctor|auto-sync|log|undo|version|help|conflict-analyze|conflict-resolve|config|history|test|workspace|ws|smart-sync|session)
|
|
42
|
+
init|setup|doctor|auto-sync|log|undo|version|help|conflict-analyze|conflict-resolve|config|history|test|workspace|ws|smart-sync|session|report)
|
|
43
43
|
cmd="${words[i]}"
|
|
44
44
|
;;
|
|
45
45
|
*)
|
|
@@ -94,10 +94,20 @@ _bingo_light() {
|
|
|
94
94
|
return
|
|
95
95
|
fi
|
|
96
96
|
|
|
97
|
+
# Inside "conflict-resolve"
|
|
98
|
+
if [[ "$cmd" == "conflict-resolve" ]]; then
|
|
99
|
+
COMPREPLY=( $(compgen -W "--verify --content-stdin --help -h" -- "$cur") )
|
|
100
|
+
return
|
|
101
|
+
fi
|
|
102
|
+
|
|
97
103
|
# Inside "dep" -- complete subcommands
|
|
98
104
|
if [[ "$cmd" == "dep" ]]; then
|
|
99
105
|
if [[ -z "$subcmd" ]]; then
|
|
100
|
-
COMPREPLY=( $(compgen -W "patch apply sync status list drop" -- "$cur") )
|
|
106
|
+
COMPREPLY=( $(compgen -W "patch apply sync status list drop override fork" -- "$cur") )
|
|
107
|
+
elif [[ "$subcmd" == "override" ]]; then
|
|
108
|
+
COMPREPLY=( $(compgen -W "list check add drop" -- "$cur") )
|
|
109
|
+
elif [[ "$subcmd" == "fork" ]]; then
|
|
110
|
+
COMPREPLY=( $(compgen -W "list check sync" -- "$cur") )
|
|
101
111
|
else
|
|
102
112
|
COMPREPLY=( $(compgen -W "--help -h" -- "$cur") )
|
|
103
113
|
fi
|
|
@@ -86,6 +86,7 @@ complete -c bingo-light -n __bingo_light_needs_command -a test -d 'Run config
|
|
|
86
86
|
complete -c bingo-light -n __bingo_light_needs_command -a workspace -d 'Manage multiple forks'
|
|
87
87
|
complete -c bingo-light -n __bingo_light_needs_command -a smart-sync -d 'Smart sync with circuit breaker and partial state'
|
|
88
88
|
complete -c bingo-light -n __bingo_light_needs_command -a session -d 'Manage session memory'
|
|
89
|
+
complete -c bingo-light -n __bingo_light_needs_command -a report -d 'Generate fork health report'
|
|
89
90
|
|
|
90
91
|
# Short aliases
|
|
91
92
|
complete -c bingo-light -n __bingo_light_needs_command -a p -d 'Alias for patch'
|
|
@@ -123,9 +124,11 @@ complete -c bingo-light -n '__bingo_light_using_command version' -s h -l help
|
|
|
123
124
|
|
|
124
125
|
# ---- conflict-resolve flags ----
|
|
125
126
|
complete -c bingo-light -n '__bingo_light_using_command conflict-resolve' -s h -l help -d 'Show help'
|
|
127
|
+
complete -c bingo-light -n '__bingo_light_using_command conflict-resolve' -l verify -d 'Run test.command after the final rebase continues'
|
|
128
|
+
complete -c bingo-light -n '__bingo_light_using_command conflict-resolve' -l content-stdin -d 'Read resolved content from stdin'
|
|
126
129
|
|
|
127
130
|
# ---- help: complete with command names ----
|
|
128
|
-
complete -c bingo-light -n '__bingo_light_using_command help' -a 'init setup patch dep sync status doctor auto-sync log undo diff version conflict-analyze conflict-resolve config history test workspace smart-sync session' -d 'Command'
|
|
131
|
+
complete -c bingo-light -n '__bingo_light_using_command help' -a 'init setup patch dep sync status doctor auto-sync log undo diff version conflict-analyze conflict-resolve config history test workspace smart-sync session report' -d 'Command'
|
|
129
132
|
|
|
130
133
|
# ---- patch subcommands (also alias "p") ----
|
|
131
134
|
complete -c bingo-light -n __bingo_light_patch_needs_subcommand -a new -d 'Create a new patch'
|
|
@@ -138,6 +141,12 @@ complete -c bingo-light -n __bingo_light_patch_needs_subcommand -a import -d 'I
|
|
|
138
141
|
complete -c bingo-light -n __bingo_light_patch_needs_subcommand -a reorder -d 'Reorder the patch stack'
|
|
139
142
|
complete -c bingo-light -n __bingo_light_patch_needs_subcommand -a squash -d 'Squash two patches into one'
|
|
140
143
|
complete -c bingo-light -n __bingo_light_patch_needs_subcommand -a meta -d 'Get/set patch metadata'
|
|
144
|
+
complete -c bingo-light -n __bingo_light_patch_needs_subcommand -a lock -d 'Lock a patch for exclusive editing'
|
|
145
|
+
complete -c bingo-light -n __bingo_light_patch_needs_subcommand -a unlock -d 'Unlock a patch'
|
|
146
|
+
complete -c bingo-light -n __bingo_light_patch_needs_subcommand -a check -d 'Check if patches are still needed'
|
|
147
|
+
complete -c bingo-light -n __bingo_light_patch_needs_subcommand -a upstream -d 'Export patch as PR-ready diff'
|
|
148
|
+
complete -c bingo-light -n __bingo_light_patch_needs_subcommand -a expire -d 'List expired patches'
|
|
149
|
+
complete -c bingo-light -n __bingo_light_patch_needs_subcommand -a stats -d 'Show patch health metrics'
|
|
141
150
|
|
|
142
151
|
# Patch short aliases
|
|
143
152
|
complete -c bingo-light -n __bingo_light_patch_needs_subcommand -a ls -d 'Alias for list'
|
|
@@ -163,6 +172,14 @@ complete -c bingo-light -n '__bingo_light_patch_using_subcommand import'
|
|
|
163
172
|
complete -c bingo-light -n '__bingo_light_patch_using_subcommand reorder' -s h -l help -d 'Show help'
|
|
164
173
|
complete -c bingo-light -n '__bingo_light_patch_using_subcommand squash' -s h -l help -d 'Show help'
|
|
165
174
|
complete -c bingo-light -n '__bingo_light_patch_using_subcommand meta' -s h -l help -d 'Show help'
|
|
175
|
+
complete -c bingo-light -n '__bingo_light_patch_using_subcommand lock' -s h -l help -d 'Show help'
|
|
176
|
+
complete -c bingo-light -n '__bingo_light_patch_using_subcommand lock' -l reason -d 'Reason for locking'
|
|
177
|
+
complete -c bingo-light -n '__bingo_light_patch_using_subcommand unlock' -s h -l help -d 'Show help'
|
|
178
|
+
complete -c bingo-light -n '__bingo_light_patch_using_subcommand unlock' -l force -d 'Force unlock even if locked by someone else'
|
|
179
|
+
complete -c bingo-light -n '__bingo_light_patch_using_subcommand check' -s h -l help -d 'Show help'
|
|
180
|
+
complete -c bingo-light -n '__bingo_light_patch_using_subcommand upstream' -s h -l help -d 'Show help'
|
|
181
|
+
complete -c bingo-light -n '__bingo_light_patch_using_subcommand expire' -s h -l help -d 'Show help'
|
|
182
|
+
complete -c bingo-light -n '__bingo_light_patch_using_subcommand stats' -s h -l help -d 'Show help'
|
|
166
183
|
|
|
167
184
|
# ---- dep subcommands ----
|
|
168
185
|
|
|
@@ -185,7 +202,9 @@ complete -c bingo-light -n __bingo_light_dep_needs_subcommand -a apply -d 'Re-a
|
|
|
185
202
|
complete -c bingo-light -n __bingo_light_dep_needs_subcommand -a sync -d 'Re-apply after update, detect conflicts'
|
|
186
203
|
complete -c bingo-light -n __bingo_light_dep_needs_subcommand -a status -d 'Show patch health'
|
|
187
204
|
complete -c bingo-light -n __bingo_light_dep_needs_subcommand -a list -d 'List all dependency patches'
|
|
188
|
-
complete -c bingo-light -n __bingo_light_dep_needs_subcommand -a drop
|
|
205
|
+
complete -c bingo-light -n __bingo_light_dep_needs_subcommand -a drop -d 'Remove a dependency patch'
|
|
206
|
+
complete -c bingo-light -n __bingo_light_dep_needs_subcommand -a override -d 'Manage npm overrides/resolutions'
|
|
207
|
+
complete -c bingo-light -n __bingo_light_dep_needs_subcommand -a fork -d 'Track fork-as-dependency drift'
|
|
189
208
|
|
|
190
209
|
# ---- workspace subcommands (also alias "ws") ----
|
|
191
210
|
|
|
@@ -219,3 +238,5 @@ complete -c bingo-light -n '__bingo_light_using_command test' -s h -
|
|
|
219
238
|
complete -c bingo-light -n '__bingo_light_using_command workspace ws' -s h -l help -d 'Show help'
|
|
220
239
|
complete -c bingo-light -n '__bingo_light_using_command smart-sync' -s h -l help -d 'Show help'
|
|
221
240
|
complete -c bingo-light -n '__bingo_light_using_command session' -s h -l help -d 'Show help'
|
|
241
|
+
complete -c bingo-light -n '__bingo_light_using_command report' -s h -l help -d 'Show help'
|
|
242
|
+
complete -c bingo-light -n '__bingo_light_using_command doctor' -l report -d 'Include team, expiry, and dep checks'
|
|
@@ -30,6 +30,7 @@ _bingo-light() {
|
|
|
30
30
|
'workspace:Manage multiple forks'
|
|
31
31
|
'smart-sync:Smart sync with circuit breaker and partial state'
|
|
32
32
|
'session:Manage session memory'
|
|
33
|
+
'report:Generate fork health report'
|
|
33
34
|
)
|
|
34
35
|
|
|
35
36
|
local -a toplevel_aliases=(
|
|
@@ -51,6 +52,12 @@ _bingo-light() {
|
|
|
51
52
|
'reorder:Reorder the patch stack'
|
|
52
53
|
'squash:Squash two patches into one'
|
|
53
54
|
'meta:Get/set patch metadata'
|
|
55
|
+
'lock:Lock a patch for exclusive editing'
|
|
56
|
+
'unlock:Unlock a patch'
|
|
57
|
+
'check:Check if patches are still needed'
|
|
58
|
+
'upstream:Export patch as PR-ready diff'
|
|
59
|
+
'expire:List expired patches'
|
|
60
|
+
'stats:Show patch health metrics'
|
|
54
61
|
)
|
|
55
62
|
|
|
56
63
|
local -a patch_aliases=(
|
|
@@ -121,7 +128,7 @@ _bingo-light() {
|
|
|
121
128
|
list|ls)
|
|
122
129
|
_arguments $patch_list_flags
|
|
123
130
|
;;
|
|
124
|
-
new|add|create|show|edit|drop|rm|remove|export|import|reorder|squash|meta)
|
|
131
|
+
new|add|create|show|edit|drop|rm|remove|export|import|reorder|squash|meta|lock|unlock|check|upstream|expire|stats)
|
|
125
132
|
_arguments $help_flag
|
|
126
133
|
;;
|
|
127
134
|
esac
|
|
@@ -145,6 +152,8 @@ _bingo-light() {
|
|
|
145
152
|
'status:Show patch health'
|
|
146
153
|
'list:List all dependency patches'
|
|
147
154
|
'drop:Remove a dependency patch'
|
|
155
|
+
'override:Manage npm overrides/resolutions'
|
|
156
|
+
'fork:Track fork-as-dependency drift'
|
|
148
157
|
)
|
|
149
158
|
_arguments -C \
|
|
150
159
|
'(- *)'{-h,--help}'[Show help]' \
|
|
@@ -175,7 +184,14 @@ _bingo-light() {
|
|
|
175
184
|
;;
|
|
176
185
|
esac
|
|
177
186
|
;;
|
|
178
|
-
|
|
187
|
+
conflict-resolve)
|
|
188
|
+
_arguments \
|
|
189
|
+
'--verify[Run test.command after the final rebase continues]' \
|
|
190
|
+
'--content-stdin[Read resolved content from stdin]' \
|
|
191
|
+
$help_flag \
|
|
192
|
+
'*:file:_files'
|
|
193
|
+
;;
|
|
194
|
+
init|setup|doctor|auto-sync|log|undo|version|conflict-analyze|config|history|test|smart-sync|session|report)
|
|
179
195
|
_arguments $help_flag
|
|
180
196
|
;;
|
|
181
197
|
help)
|