feat: add shallow fetch and grouped skill selection

This commit is contained in:
2026-07-29 10:57:53 +08:00
parent 18ebb70911
commit 262c138bee
6 changed files with 174 additions and 10 deletions
+21 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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