core/tools/vcs: add read-only vcs_* agent tools

Expose vcs_kind/status/diff/log/branch over detect_vcs; cover git and
custom detector registration.
This commit is contained in:
2026-07-15 10:22:00 +08:00
parent c402555778
commit 5b4d3985b7
3 changed files with 207 additions and 0 deletions
+11
View File
@@ -0,0 +1,11 @@
from .backend import VcsBackend as VcsBackend
from .detect import clear_extra_detectors as clear_extra_detectors
from .detect import detect_vcs as detect_vcs
from .detect import register_detector as register_detector
from .git_backend import GitBackend as GitBackend
from .tools import VCS_TOOLS as VCS_TOOLS
from .tools import vcs_branch as vcs_branch
from .tools import vcs_diff as vcs_diff
from .tools import vcs_kind as vcs_kind
from .tools import vcs_log as vcs_log
from .tools import vcs_status as vcs_status
+86
View File
@@ -0,0 +1,86 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from plyngent.agent import tool
from plyngent.tools.workspace import WorkspaceError, get_workspace_root
from .detect import detect_vcs
if TYPE_CHECKING:
from .backend import VcsBackend
def _backend_or_error() -> VcsBackend | str:
try:
root = get_workspace_root()
except WorkspaceError as exc:
return f"error: {exc}"
backend = detect_vcs(root)
if backend is None:
return (
"error: no supported VCS detected under workspace "
"(currently: git; other systems can register detectors)"
)
return backend
@tool
def vcs_kind() -> str:
"""Return the detected VCS kind under the workspace (e.g. ``git``), or an error."""
backend = _backend_or_error()
if isinstance(backend, str):
return backend
return backend.kind
@tool
def vcs_status() -> str:
"""Show working-tree status for the detected VCS (read-only)."""
backend = _backend_or_error()
if isinstance(backend, str):
return backend
return backend.status()
@tool
def vcs_diff(path: str = "", *, staged: bool = False) -> str:
"""Show a unified diff for the detected VCS (read-only).
``path`` is optional and relative to the workspace. ``staged=true`` is
honored by git (index vs HEAD); other backends may ignore it.
"""
backend = _backend_or_error()
if isinstance(backend, str):
return backend
rel = path.strip() or None
return backend.diff(staged=staged, path=rel)
@tool
def vcs_log(limit: int = 10) -> str:
"""Show recent commits for the detected VCS (read-only)."""
if limit < 1:
return "error: limit must be >= 1"
backend = _backend_or_error()
if isinstance(backend, str):
return backend
return backend.log(limit=limit)
@tool
def vcs_branch() -> str:
"""Show the current branch / named head for the detected VCS (read-only)."""
backend = _backend_or_error()
if isinstance(backend, str):
return backend
return backend.branch()
VCS_TOOLS = [
vcs_kind,
vcs_status,
vcs_diff,
vcs_log,
vcs_branch,
]