feat: add shallow fetch and grouped skill selection
This commit is contained in:
+21
-2
@@ -150,7 +150,7 @@ def _ensure_registry_fetched(name: str, entry: dict[str, object]) -> None:
|
||||
)
|
||||
_print(f"拉取外部 skill: {name}")
|
||||
subprocess.run(
|
||||
["git", "clone", "--branch", ref, "--", repo, str(dest)],
|
||||
["git", "clone", "--depth", "1", "--branch", ref, "--", repo, str(dest)],
|
||||
check=True,
|
||||
)
|
||||
if not discover_external_skills(name, entry):
|
||||
@@ -563,6 +563,7 @@ def cmd_select(args: argparse.Namespace) -> None:
|
||||
expected: Path,
|
||||
kind: str,
|
||||
description: str,
|
||||
indent: int = 0,
|
||||
) -> SkillChoice:
|
||||
return SkillChoice(
|
||||
name=name,
|
||||
@@ -579,6 +580,7 @@ def cmd_select(args: argparse.Namespace) -> None:
|
||||
if project_root is not None
|
||||
else ""
|
||||
),
|
||||
indent=indent,
|
||||
)
|
||||
|
||||
choices = [
|
||||
@@ -628,9 +630,26 @@ def cmd_select(args: argparse.Namespace) -> None:
|
||||
expected=expected,
|
||||
kind=f"external:{package}",
|
||||
description=description,
|
||||
indent=1,
|
||||
)
|
||||
)
|
||||
choice_requests[choice_name] = (skill_name, f"registry:{package}")
|
||||
child_names = tuple(
|
||||
f"{package}/{skill_name}"
|
||||
for skill_name in skill_names
|
||||
if f"{package}/{skill_name}" in choice_requests
|
||||
)
|
||||
if child_names:
|
||||
first_child = len(choices) - len(child_names)
|
||||
choices.insert(
|
||||
first_child,
|
||||
SkillChoice(
|
||||
name=package,
|
||||
kind="repository",
|
||||
description=str(entry.get("description", "")),
|
||||
children=child_names,
|
||||
),
|
||||
)
|
||||
|
||||
try:
|
||||
scope_label = (
|
||||
@@ -807,7 +826,7 @@ def cmd_fetch(args: argparse.Namespace) -> None:
|
||||
else:
|
||||
_print(f"克隆: {repo} -> {dest}")
|
||||
subprocess.run(
|
||||
["git", "clone", "--branch", ref, "--", repo, str(dest)],
|
||||
["git", "clone", "--depth", "1", "--branch", ref, "--", repo, str(dest)],
|
||||
check=True,
|
||||
)
|
||||
|
||||
|
||||
+26
-5
@@ -14,6 +14,8 @@ class SkillChoice:
|
||||
description: str = ""
|
||||
installed: bool = False
|
||||
readonly_status: str = ""
|
||||
children: tuple[str, ...] = ()
|
||||
indent: int = 0
|
||||
|
||||
|
||||
def fit_to_width(text: str, width: int) -> str:
|
||||
@@ -74,7 +76,19 @@ def select_skills(
|
||||
except ImportError as exc:
|
||||
raise RuntimeError("当前 Python 环境不支持 curses,无法打开交互界面") from exc
|
||||
|
||||
selected = {choice.name for choice in choices if choice.installed}
|
||||
selected = {
|
||||
choice.name
|
||||
for choice in choices
|
||||
if choice.installed and not choice.children
|
||||
}
|
||||
|
||||
def mark_for(choice: SkillChoice) -> str:
|
||||
if not choice.children:
|
||||
return "x" if choice.name in selected else " "
|
||||
selected_children = selected.intersection(choice.children)
|
||||
if len(selected_children) == len(choice.children):
|
||||
return "x"
|
||||
return "-" if selected_children else " "
|
||||
|
||||
def draw(stdscr: object) -> set[str] | None:
|
||||
curses.curs_set(0)
|
||||
@@ -122,11 +136,11 @@ def select_skills(
|
||||
for offset, choice in enumerate(visible[start : start + item_capacity]):
|
||||
index = start + offset
|
||||
row = 3 + offset * 3
|
||||
mark = "x" if choice.name in selected else " "
|
||||
mark = mark_for(choice)
|
||||
description = f" {choice.description}" if choice.description else ""
|
||||
focus_attr = curses.A_REVERSE if index == current else curses.A_NORMAL
|
||||
parts = [
|
||||
(f"[{mark}] ", focus_attr),
|
||||
(f"{' ' * choice.indent}[{mark}] ", focus_attr),
|
||||
(choice.name, focus_attr | curses.A_BOLD),
|
||||
(f" {choice.kind}", focus_attr | source_attr),
|
||||
]
|
||||
@@ -171,8 +185,15 @@ def select_skills(
|
||||
elif key == curses.KEY_DOWN and visible:
|
||||
current = (current + 1) % len(visible)
|
||||
elif key == " " and visible:
|
||||
name = visible[current].name
|
||||
selected.symmetric_difference_update({name})
|
||||
choice = visible[current]
|
||||
if choice.children:
|
||||
children = set(choice.children)
|
||||
if children.issubset(selected):
|
||||
selected.difference_update(children)
|
||||
else:
|
||||
selected.update(children)
|
||||
else:
|
||||
selected.symmetric_difference_update({choice.name})
|
||||
elif key == "/":
|
||||
curses.curs_set(1)
|
||||
query = _read_query(stdscr, curses, width, row=2)
|
||||
|
||||
+11
-1
@@ -100,7 +100,17 @@ def fetch_source(name: str, entry: dict[str, Any]) -> Path:
|
||||
else:
|
||||
checkout.parent.mkdir(parents=True, exist_ok=True)
|
||||
subprocess.run(
|
||||
["git", "clone", "--branch", ref, "--", str(repo), str(checkout)],
|
||||
[
|
||||
"git",
|
||||
"clone",
|
||||
"--depth",
|
||||
"1",
|
||||
"--branch",
|
||||
ref,
|
||||
"--",
|
||||
str(repo),
|
||||
str(checkout),
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
return checkout
|
||||
|
||||
Reference in New Issue
Block a user